摘要:轉載請注明出處翻譯使用文檔使用諸如和之類的工具來實現或服務的客戶端此外允許你在庫如之上編寫自己的代碼通過自定義解碼器和錯誤處理可以用最小的開銷和最少的代碼將你的代碼關聯到任何基于文本的接口是通過將注解轉換成模板請求來實現它的功能的可以將請求
轉載請注明出處: 翻譯: Spring Cloud Feign使用文檔
Why Feign and not X?Feign使用諸如Jersey和CXF之類的工具來實現ReST或SOAP服務的java客戶端, 此外, Feign允許你在http庫(如: Apache HC)之上編寫自己的代碼. 通過自定義解碼器(decoders)和錯誤處理(error handing), Feign可以用最小的開銷和最少的代碼將你的代碼關聯到任何基于文本的http接口(http APIS),
How does Feign work?Feign是通過將注解(annotations)轉換成模板請求來實現它的功能的, Feign可以將請求參數直接應用到這些模板上. 盡管Feign只支持基于文本的接口, 但同樣的它能顯著地簡化系統的方方面面, 如請求重放等, 此外, Feign也可以使你的單元測試更加簡單.
Java Version CampatibilityFeign 10.x及以上的版本是基于Java 8構建的, 且應該同樣支持Java 9、10、11, 如果你需要在JDK 6的版本上使用的話, 請使用Feign 9.x版本.
Basics下面的代碼是適配Retrofit示例的用法:
interface GitHub { @RequestLine("GET /repos/{owner}/{repo}/contributors") ListInterface Annotationscontributors(@Param("owner") String owner, @Param("repo") String repo); } public static class Contributor { String login; int contributions; } public class MyApp { public static void main(String... args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); // Fetch and print a list of the contributors to this library. List contributors = github.contributors("OpenFeign", "feign"); for (Contributor contributor : contributors) { System.out.println(contributor.login + " (" + contributor.contributions + ")"); } } }
Feign的注解定義了接口與底層http客戶端功能之間的約定, 默認情況下各個注解的約定含義如下:
Annotation | Interface Target | Usage |
---|---|---|
@RequestLine | 接口 | 定義請求的HttpMethod和UriTemplate. 模板中可以使用大括號包圍的表達式({expression}), 表達式的值由@Param對應參數的注解值提供. |
@Param | 參數 | 定義模板變量, 變量的值應該由名字相對應的表達式提供. |
@Headers | 方法、Type | 定義HeaderTemplate; 使用@Param注解的值解析對應的表達式. 當該注解應用在Type上時, 該模板會被應用到每一個請求上. 當該注解應用在方法上時, 該模板僅會被應用到對應的方法上. |
@QueryMap | 參數 | 將鍵值對類型的Map、POJO展開成地址上的請求參數(query string) |
@HeaderMap | 參數 | 將鍵值對類型的Map展開成請求頭Http Headers. |
@Body | 方法 | 定義與UriTemplate和HeaderTemplate類似的模板(Template), 該模板可以使用@Param的注解值解析對應的表達式 |
Feign支持由URI Template - RFC 6570定義的簡單字符串(Level 1)表達式, 表達式的值從相關方法上對應@Param注解提供, 示例如下:
public interface GitHub { @RequestLine("GET /repos/{owner}/{repo}/contributors") ListgetContributors(@Param("owner") String owner, @Param("repo") String repository); class Contributor { String login; int contributions; } } public class MyApp { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); /* The owner and repository parameters will be used to expand the owner and repo expressions * defined in the RequestLine. * * the resulting uri will be https://api.github.com/repos/OpenFeign/feign/contributors */ github.contributors("OpenFeign", "feign"); } }
表達式必須使用大括號({})包裹著, 并且支持使用冒號(:)分隔的正則表達式來限定表達式的值. 如限定上述例子的owner參數的值必須是字母: {owner:[a-zA-Z]*}.
Request Parameter ExpansionRequestLine和QueryMap遵循 URI Template - RFC 6570規范對一級模板(Level 1 templates)的規定:
未被解析的值將會被忽略.
所有未編碼或者通過@Param注解標記為已編碼(encoded)的字符和變量值都使用pct編碼(pct-encoded).
可以從Advanced Usage一節查看更多示例.
What about slashes? /Custom Expansion默認情況下, @RequestLine和@QueryMap模板不會對正斜杠/進行編碼, 如果需要默認對其進行編碼的話, 可以將@RequestLine的decodeSlash屬性值設置為false.
What about plus? +
根據URI規范, +可以使用在URI地址和請求參數(query segments)這兩個部分上, 然而在請求參數(query)上對該符號的處理卻有可能不一致, 在一些遺留的系統上, +會被解析成一個空白符(space). 對此, Feign采用現代系統對+的解釋, 不會將+認為是一個空白符(space), 并將請求參數上的+編碼為%2B.
如果你希望將+當成空白符(space), 那么請直接使用一個空格 或者直接將其編碼為%20.
@Param注解有一個可選的參數expander可以用來控制單個參數的展開行為(expansion), 該屬性的值必須指向一個實現了Expander接口的類:
public interface Expander { String expand(Object value); }
對該方法的返回值的處理與上述規則相同, 如果返回值是null或者是一個空字符串, 那么該值會被忽略. 如果返回值不是使用pct編碼(pct-encoded)的, 將會自動轉換成pct編碼. 可以從 Custom @Param Expansion 一節查看更多示例.
Request Headers Expansion@Headers和HeaderMap模板對 Request Parameter Expansion 一節闡述的規則做以下修改, 并遵循之:
未被解析的值將會被忽略, 但如果解析到一個空的值(empty header value), 那么對應的請求頭會被移除.
不會對請求頭使用pct編碼(pct-encoding).
可以從Headers一節查看示例.
Reuqest Body Expansion關于@Param參數和參數名需要注意的點
無論是在@RequestLine、@QueryMap、@BodyTemplate還是@Headers上的表達式, 只要表達式內的變量名字相同, 那么它們的值也必然相同. 如下面的例子, contentType的值會同時被解析到請求頭(header)和路徑(path)上:
public interface ContentService { @RequestLine("GET /api/documents/{contentType}") @Headers("Accept: {contentType}") String getDocumentByType(@Param("contentType") String type); }當你在設計你的接口的一定要牢記這一點.
Body模板對 Request Parameter Expansion 一節闡述的規則做以下修改, 并遵循之:
未被解析的值將會被忽略.
展開的值在被解析到請求體之前不會經過Encoder處理.
必須指定Content-Type請求頭, 可以從 Body Templates一節查看示例.
Customization你可以在很多地方對Feign進行定制. 比如, 你可以使用Feign.builder()對自定義的組件構建API接口:
interface Bank { @RequestLine("POST /account/{id}") Account getAccountInfo(@Param("id") String id); } public class BankService { public static void main(String[] args) { Bank bank = Feign.builder().decoder( new AccountDecoder()) .target(Bank.class, "https://api.examplebank.com"); } }Multiple Interfaces
Feign客戶以對使用Target
例如, 下面的代碼可以實現為從身份服務中獲取當前url和授權令牌(auth token), 然后設置到每個請求上:
public class CloudService { public static void main(String[] args) { CloudDNS cloudDNS = Feign.builder() .target(new CloudIdentityTargetExamples(user, apiKey)); } class CloudIdentityTarget extends Target { /* implementation of a Target */ } }
Feign包含了GitHub和Wikipedia的客戶端示例代碼, 在實踐中也可以參考這些項目, 尤其是example daemon.
IntegrationsFeign在設計上就希望能夠和其他開源項目很好的整合到一起, 我們也很樂于將你喜歡的模塊添加進來.
GsonGson包含了和JSON接口相關的編碼(GsonEncoder)、解碼器(GsonDecoder), 將它將它用到Feign.Builder的方式如下:
public class Example { public static void main(String[] args) { GsonCodec codec = new GsonCodec(); GitHub github = Feign.builder() .encoder(new GsonEncoder()) .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); } }Jackson
Jackson包含了和JSON接口相關的編碼(JacksonEncoder)、解碼器(JacksonDecoder), 將它將它用到Feign.Builder的方式如下:
public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .encoder(new JacksonEncoder()) .decoder(new JacksonDecoder()) .target(GitHub.class, "https://api.github.com"); } }Sax
SaxDecoder提供了可以與普通JVM和Android環境兼容的方式解析XML文本, 下面的例子展示了如何使用:
public class Example { public static void main(String[] args) { Api api = Feign.builder() .decoder(SAXDecoder.builder() .registerContentHandler(UserIdHandler.class) .build()) .target(Api.class, "https://apihost"); } }JAXB
JAXB包含了和XML接口相關的編碼器(JAXBEncoder)、解碼器(JAXBEncoder), 將它將它用到Feign.Builder的方式如下:
public class Example { public static void main(String[] args) { Api api = Feign.builder() .encoder(new JAXBEncoder()) .decoder(new JAXBDecoder()) .target(Api.class, "https://apihost"); } }JAX-RS
JAXRSContract使用JAX-RS規范提供的標準覆蓋了對注解的處理, 目前實現的是1.1版的規范, 示例如下:
interface GitHub { @GET @Path("/repos/{owner}/{repo}/contributors") ListOkHttpcontributors(@PathParam("owner") String owner, @PathParam("repo") String repo); } public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .contract(new JAXRSContract()) .target(GitHub.class, "https://api.github.com"); } }
OkHttpClient直接將Feign的http請求直接交由OkHttp處理, 后者實現了SPDY協議和提供了更好的網絡控制能力.
將OkHttp整合到Feign中需要你把OkHttp模塊放到classpath下, 然后做如下配置:
public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .client(new OkHttpClient()) .target(GitHub.class, "https://api.github.com"); } }Ribbon
RibbonClient會覆蓋Feign客戶端的URL解析, 以實現由Ribbon提供的智能路由和彈性能力.
將Ribbon與Feign整合需要你將url中的主機名(host)部分替換成Ribbon客戶端名. 例如Ribbon客戶端明為myAppProd:
public class Example { public static void main(String[] args) { MyService api = Feign.builder() .client(RibbonClient.create()) .target(MyService.class, "https://myAppProd"); } }Java 11 Http2
Http2Client直接將Feign的http請求交給Java11 New HTTP/2 Client處理, 后者實現了HTTP/2協議.
要將New HTTP/2 Client與Feign整合使用, 你需要使用Java SDK 11, 并做如下配置:
GitHub github = Feign.builder() .client(new Http2Client()) .target(GitHub.class, "https://api.github.com");Hystrix
HystrixFeign實現了由Hystrix提供的斷路器功能.
要將Hystrix與Feign整合, 你需要將Hystrix模塊放到classpath下, 并使用HystrixFeign:
public class Example { public static void main(String[] args) { MyService api = HystrixFeign.builder().target(MyService.class, "https://myAppProd"); } }SOAP
SOAP包含了XML接口相關的編碼器(SOAPEncoder)、解碼器(SOAPDecoder).
該模塊通過JAXB和SOAPMessage實現了對SOAP Body的編碼和解碼的支持, 通過將SOAPFault包裝秤javax.xml.ws.soap.SOAPFaultException實現了對SOAPFault解碼的功能, 因此, 對于SOAPFault的處理, 你只需要捕獲SOAPFaultException.
使用示例如下:
public class Example { public static void main(String[] args) { Api api = Feign.builder() .encoder(new SOAPEncoder(jaxbFactory)) .decoder(new SOAPDecoder(jaxbFactory)) .errorDecoder(new SOAPErrorDecoder()) .target(MyApi.class, "http://api"); } }
如果SOAP Faults的響應使用了表示錯誤的狀態碼(4xx, 5xx, …)的話, 那么你還需要添加一個SOAPErrorDecoder.
SLF4JSLF4JModule實現了將Feign的日志重定向到SLF4J, 這允許你很容易的就能使用你想用的日志后端(Logback、Log4J等).
要將SLF4J與Feign整合, 你需要將SLF4J模塊和對應的日志后端模塊放到classpath下, 并做如下配置:
public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .logger(new Slf4jLogger()) .target(GitHub.class, "https://api.github.com"); } }Decoders
Feign.builder()允許你手動指定額外的配置, 如配置如何對響應進行解析.
如果你接口定義的方法的返回值是除了Response、String、byte[]或void之外的類型, 那么你必須配置一個非默認的Decoder.
下面的代碼展示了如何配置使用feign-gson對JSON解碼:
public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); } }
如果你想在對響應進行解碼之前先對其做處理的話, 你可以使用mapAndDecode方法, 下面的代碼展示了對一個jsonp響應的處理, 在將響應交給JSON解碼器之前, 需要先對jsonp做處理:
public class Example { public static void main(String[] args) { JsonpApi jsonpApi = Feign.builder() .mapAndDecode((response, type) -> jsopUnwrap(response, type), new GsonDecoder()) .target(JsonpApi.class, "https://some-jsonp-api.com"); } }Encoders
將一個請求體發送到服務器的最簡單的辦法是定義一個POST請求方法, 該方法的參數類型是String或byte[], 且參數上不帶任何注解, 并且你可能還需要設置Content-Type請求頭(如果沒有的話):
interface LoginClient { @RequestLine("POST /") @Headers("Content-Type: application/json") void login(String content); } public class Example { public static void main(String[] args) { client.login("{"user_name": "denominator", "password": "secret"}"); } }
而通過配置Encoder, 你可以發送一個類型安全的請求體, 下面的例子展示了使用feign-gson擴展來實現編碼:
static class Credentials { final String user_name; final String password; Credentials(String user_name, String password) { this.user_name = user_name; this.password = password; } } interface LoginClient { @RequestLine("POST /") void login(Credentials creds); } public class Example { public static void main(String[] args) { LoginClient client = Feign.builder() .encoder(new GsonEncoder()) .target(LoginClient.class, "https://foo.com"); client.login(new Credentials("denominator", "secret")); } }@Body templates
使用@Body注解的模板會使用@Param注解的值來展開模板內部的表達式, 對于POST請求你可能還需要設置Content-Type請求頭(如果沒有的話):
interface LoginClient { @RequestLine("POST /") @Headers("Content-Type: application/xml") @Body("Headers") void xml(@Param("user_name") String user, @Param("password") String password); @RequestLine("POST /") @Headers("Content-Type: application/json") // json curly braces must be escaped! @Body("%7B"user_name": "{user_name}", "password": "{password}"%7D") void json(@Param("user_name") String user, @Param("password") String password); } public class Example { public static void main(String[] args) { client.xml("denominator", "secret"); // client.json("denominator", "secret"); // {"user_name": "denominator", "password": "secret"} } }
Feign支持在api上為每個請求設置請求頭, 也支持為每個客戶端的請求設置請求頭, 你可以根據實際場景進行選擇.
Set headers using apis對于那些明確需要設置某些請求頭的接口的情況, 適用于將請求頭的定義作為接口的一部分.
靜態配置的請求頭可以通過在接口上使用@Headers注解設置:
@Headers("Accept: application/json") interface BaseApi{ @Headers("Content-Type: application/json") @RequestLine("PUT /api/{key}") void put(@Param("key") String key, V value); }
也可以在方法上的@Headers使用變量展開動態指定請求頭的內容:
public interface Api { @RequestLine("POST /") @Headers("X-Ping: {token}") void post(@Param("token") String token); }
有時候, 對于同一個接口或客戶端的請求頭, 其鍵和值可能會隨著不同的方法調用而發生變化, 且不可預知(例如: 自定義元數據請求頭字段"x-amz-meta-"或"x-goog-meta-"), 此時可以在接口上聲明一個Map參數, 并使用@HeaderMap注解將Map的內容設置為對應請求的請求頭:
public interface Api { @RequestLine("POST /") void post(@HeaderMap MapheaderMap); }
上述的幾個方法都可以在接口上指定請求的請求頭, 且不需要在構造時對Feign客戶端做任何的定制.
Setting headers per target當同一個接口的請求需要針對不同的請求對象(endpoints)配置不同的請求頭, 或者需要對同一個接口的每個請求都定制其請求頭時, 可以在Feign客戶端上使用RequestInterceptor或Target來設置請求頭.
使用RequestInterceptor設置請求頭的例子可以在Request Interceptor一節中查看示例.
使用Target設置請求頭的示例如下:
static class DynamicAuthTokenTargetimplements Target { public DynamicAuthTokenTarget(Class clazz, UrlAndTokenProvider provider, ThreadLocal requestIdProvider); @Override public Request apply(RequestTemplate input) { TokenIdAndPublicURL urlAndToken = provider.get(); if (input.url().indexOf("http") != 0) { input.insert(0, urlAndToken.publicURL); } input.header("X-Auth-Token", urlAndToken.tokenId); input.header("X-Request-ID", requestIdProvider.get()); return input.request(); } } public class Example { public static void main(String[] args) { Bank bank = Feign.builder() .target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider)); } }
上述方法的最終效果取決于你對RequestInterceptor或Target內部的實現, 可以通過這種方法對每個Feign客戶端的所有接口調用設置請求頭. 這在一些場景下是非常有用的, 如對每個Feign客戶端的所有請求設置認證令牌authentication token. 這些方法是在接口調用者所在的線程中執行的(譯者注: 需要注意線程安全), 因此請求頭的值可以是在調用時根據上下文動態地設置. 例如, 可以根據不同的調用線程, 從ThreadLocal里讀取不同的數據設置請求頭.
Advanced usage Base Apis大多數情況下服務的接口都遵循相同的約定. Feign使用單繼承的方式來實現, 比如下面的例子:
interface BaseAPI { @RequestLine("GET /health") String health(); @RequestLine("GET /all") Listall(); }
你可以通過繼承的方式來擁有BaseAPI的接口, 并實現其他特定的接口:
interface CustomAPI extends BaseAPI { @RequestLine("GET /custom") String custom(); }
很多時候, 接口對資源的表示也是一致的, 因此, 也可以在基類的接口中使用泛型參數:
@Headers("Accept: application/json") interface BaseApiLogging{ @RequestLine("GET /api/{key}") V get(@Param("key") String key); @RequestLine("GET /api") List list(); @Headers("Content-Type: application/json") @RequestLine("PUT /api/{key}") void put(@Param("key") String key, V value); } interface FooApi extends BaseApi { } interface BarApi extends BaseApi { }
你可以通過為Feign客戶端設置Logger來記錄其http日志, 最簡單的實現如下:
public class Example { public static void main(String[] args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .logger(new Logger.JavaLogger().appendToFile("logs/http.log")) .logLevel(Logger.Level.FULL) .target(GitHub.class, "https://api.github.com"); } }Request Interceptors
如果你需要跨Feign客戶端對所有請求都做修改, 那么你可以配置RequestInterceptor來實現. 例如, 如果你是請求的一個代理, 那么你可能會需要設置X-Forwarded-For請求頭:
static class ForwardedForInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { template.header("X-Forwarded-For", "origin.host.com"); } } public class Example { public static void main(String[] args) { Bank bank = Feign.builder() .decoder(accountDecoder) .requestInterceptor(new ForwardedForInterceptor()) .target(Bank.class, "https://api.examplebank.com"); } }
另一個常見的使用攔截器的場景是授權, 比如使用內置的BasicAuthRequestInterceptor:
public class Example { public static void main(String[] args) { Bank bank = Feign.builder() .decoder(accountDecoder) .requestInterceptor(new BasicAuthRequestInterceptor(username, password)) .target(Bank.class, "https://api.examplebank.com"); } }Custom @Param Expansion
使用@Param注解的參數會用其toString()方法展開獲得參數值, 也可以通過制定一個自定義的Param.Expander來控制. 如對日期的格式化:
public interface Api { @RequestLine("GET /?since={date}") Result list(@Param(value = "date", expander = DateToMillis.class) Date date); }Dynamic Query Parameters
可以通過對Map類型的參數加上QueryMap注解, 將Map的內容構造成查詢參數(query parameters):
public interface Api { @RequestLine("GET /find") V find(@QueryMap MapqueryMap); }
同樣的, 也可以通過使用QueryMapEncoder實現用POJO對象生成查詢參數(query parameter):
public interface Api { @RequestLine("GET /find") V find(@QueryMap CustomPojo customPojo); }
當用這種方式時, 如果沒有指定一個自定義的QueryMapEncoder, 那么查詢參數的(query parameter)內容將根據對象的成員變量生成, 參數名對應變量名. 下面的例子中, 根據POJO對象生成的查詢參數(query parameter)的內容是"/find?name={name}&number={number}", 生成的查詢參數的順序是不固定的, 按照慣例, 如果POJO對象的某個變量值為null, 那么該變量會被丟棄.
public class CustomPojo { private final String name; private final int number; public CustomPojo (String name, int number) { this.name = name; this.number = number; } }
設置自定義QueryMapEncoder的方式如下:
public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .queryMapEncoder(new MyCustomQueryMapEncoder()) .target(MyApi.class, "https://api.hostname.com"); } }
當用@QueryMao注解時, 默認的編碼器(encoder)會對對象的字段使用反射來將其展開成查詢參數(query string). 如果希望通過對象的getter和setter方法來展開查詢參數(query string), 請使用BeanQueryMapEncoder:
public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .queryMapEncoder(new BeanQueryMapEncoder()) .target(MyApi.class, "https://api.hostname.com"); } }Error Handling
你可以通過在Feign實例構造時注冊一個自定義的ErrorDecoder來實現對非正常響應的控制:
public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .errorDecoder(new MyErrorDecoder()) .target(MyApi.class, "https://api.hostname.com"); } }
所有HTTP狀態碼不為2xx的響應都會觸發ErrorDecoder的decode方法, 在這個方法內你可以對這些響應針對性地拋出異常, 或做其他額外的處理. 如果希望對請求進行重試, 那么可以拋出RetryableException, 該異常會觸發Retryer.
Retry默認情況下, Feign會對產生IOException的請求自動重試, 無論使用的是哪種HTTP方法, 都認為IOExcdeption是由短暫的網絡問題產生的. 對ErrorDecoder內拋出的RetryableException也會進行請求重試. 你也可以通在Feign實例構造時設置自定義的Retryer來定制重試行為:
public class Example { public static void main(String[] args) { MyApi myApi = Feign.builder() .retryer(new MyRetryer()) .target(MyApi.class, "https://api.hostname.com"); } }
Retryer的實現需要決定一個請求是否應該進行重試, 可以通過continueOrPropagate(RetryableException e)方法的返回值(true或false)來實現. 每個Feign客戶端執行時都會構造一個Retryer實例, 這樣的話你可以維護每個請求的重新狀態.
如果最終重試也失敗了, 那么會拋出RetryException, 如果希望拋出導致重試失敗的異常, 可以在構造Feign客戶端時指定exceptionPropagationPolicy()選項.
Static and Default Methods使用Feign的接口可能是靜態的或默認的方法(Java 8及以上支持), 這允許Feign客戶端包含一些不適用底層接口定義的邏輯. 例如, 使用靜態方法可以很輕易地指定通用客戶端構造配置, 使用默認方法可以用于組合查詢或定義默認參數:
interface GitHub { @RequestLine("GET /repos/{owner}/{repo}/contributors") Listcontributors(@Param("owner") String owner, @Param("repo") String repo); @RequestLine("GET /users/{username}/repos?sort={sort}") List repos(@Param("username") String owner, @Param("sort") String sort); default List repos(String owner) { return repos(owner, "full_name"); } /** * Lists all contributors for all repos owned by a user. */ default List contributors(String user) { MergingContributorList contributors = new MergingContributorList(); for(Repo repo : this.repos(owner)) { contributors.addAll(this.contributors(user, repo.getName())); } return contributors.mergeResult(); } static GitHub connect() { return Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73455.html
摘要:繼承支持通過單繼承接口支持樣板,這允許將通用操作分組為方便的基本接口。,記錄基本信息以及請求和響應。例如,類定義參數和以下客戶端使用注解使用類 聲明式REST客戶端:Feign Feign是一個聲明式的Web服務客戶端,它使編寫Web服務客戶端變得更容易,要使用Feign,請創建一個接口并對其進行注解,它具有可插拔的注解支持,包括Feign注解和JAX-RS注解,Feign還支持可插拔...
摘要:不過大多數講解還停留在對功能使用的層面,其底層的很多原理,很多人可能并不知曉。每個線程池里的線程就僅僅用于請求那個服務。 歡迎關注微信公眾號:石杉的架構筆記(id:shishan100) 每日更新!精品技術文章準時送上! 目錄 一、業務場景介紹 二、Spring Cloud核心組件:Eureka 三、Spring Cloud核心組件:Feign 四、Spring Cloud核心組件:R...
摘要:不過大多數講解還停留在對功能使用的層面,其底層的很多原理,很多人可能并不知曉。每個線程池里的線程就僅僅用于請求那個服務。 歡迎關注微信公眾號:石杉的架構筆記(id:shishan100) 每日更新!精品技術文章準時送上! 目錄 一、業務場景介紹 二、Spring Cloud核心組件:Eureka 三、Spring Cloud核心組件:Feign 四、Spring Cloud核心組件:R...
摘要:下一篇介紹基于的服務注冊與調用。服務提供者工程配置這里服務提供者是使用之前進階教程第三篇整合連接池以及監控改造而來,這里一樣的部分就不再重復說明,下面將說明新增的部分。 Spring Cloud簡介 Spring Cloud是一個基于Spring Boot實現的云應用開發工具,它為基于JVM的云應用開發中涉及的配置管理、服務發現、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分...
閱讀 1207·2021-09-03 10:44
閱讀 604·2019-08-30 13:13
閱讀 2796·2019-08-30 13:11
閱讀 1967·2019-08-30 12:59
閱讀 1034·2019-08-29 15:32
閱讀 1595·2019-08-29 15:25
閱讀 987·2019-08-29 12:24
閱讀 1277·2019-08-27 10:58