Feign是一個聲明式的Web服務客戶端,它使編寫Web服務客戶端變得更容易,要使用Feign,請建立一個接口並對其進行註解,它具備可插拔的註解支持,包括Feign註解和JAX-RS註解,Feign還支持可插拔編碼器和解碼器。Spring Cloud增長了對Spring MVC註解的支持,並使用了Spring Web中默認使用的相同HttpMessageConverters
,Spring Cloud集成了Ribbon和Eureka,在使用Feign時提供負載均衡的http客戶端。java
要在項目中包含Feign,請使用包含組名爲org.springframework.cloud
和工件名爲spring-cloud-starter-openfeign
的啓動器。git
spring boot應用示例github
@SpringBootApplication @EnableFeignClients public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
StoreClient.javaspring
@FeignClient("stores") public interface StoreClient { @RequestMapping(method = RequestMethod.GET, value = "/stores") List<Store> getStores(); @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json") Store update(@PathVariable("storeId") Long storeId, Store store); }
在@FeignClient
註解中,String值(上面的「stores」)是一個任意客戶端名稱,用於建立Ribbon負載均衡器,你還可使用url
屬性指定URL(絕對值或僅指定主機名),應用程序上下文中bean的名稱是接口的徹底限定名稱,要指定本身的別名值,可使用@FeignClient
註解的qualifier
值。json
上面的Ribbon客戶端將想要發現「stores」服務的物理地址,若是你的應用程序是Eureka客戶端,那麼它將解析Eureka服務註冊表中的服務,若是你不想使用Eureka,只需在外部配置中配置服務器列表。api
Spring Cloud的Feign支持的核心概念是命名客戶端,每一個feign客戶端都是一個組件集成的一部分,這些組件協同工做以按需聯繫遠程服務器,而且集成有一個名稱,做爲使用@FeignClient
註解的應用程序開發人員可使用這個名稱。Spring Cloud使用FeignClientsConfiguration
按需爲每一個命名客戶端建立一個新的集成做爲ApplicationContext
,這包含(除其餘外)feign.Decoder
、feign.Encoder
和feign.Contract
,可使用@FeignClient
註解的contextId
屬性覆蓋該集成的名稱。服務器
Spring Cloud容許你經過使用@FeignClient
聲明其餘配置(在FeignClientsConfiguration
之上)來徹底控制feign客戶端,例如:app
@FeignClient(name = "stores", configuration = FooConfiguration.class) public interface StoreClient { //.. }
在這種狀況下,客戶端由FeignClientsConfiguration
中已有的組件以及FooConfiguration
中的任何組件組成(後者將覆蓋前者)。負載均衡
FooConfiguration
不須要使用@Configuration
註解,可是,若是是,則注意將其從任何包含此配置的@ComponentScan
中排除,由於它將成爲feign.Decoder
、feign.Encoder
、feign.Contract
等的默認源。這能夠經過將其放在任何@ComponentScan
或@SpringBootApplication
的單獨的非重疊包中來避免,也能夠在@ComponentScan
中明確排除。
如今不推薦使用serviceId
屬性,而是使用name
屬性。
使用@FeignClient
註解的contextId
屬性除了更改ApplicationContext
集成的名稱,它將覆蓋客戶端名稱的別名,它將用做爲該客戶端建立的配置bean名稱的一部分。
之前,使用url
屬性不須要name
屬性,如今須要使用name
。
name
和url
屬性支持佔位符。異步
@FeignClient(name = "${feign.name}", url = "${feign.url}") public interface StoreClient { //.. }
Spring Cloud Netflix默認爲feign(BeanType
beanName:ClassName
)提供如下bean:
Decoder
feignDecoder:ResponseEntityDecoder
(包裝SpringDecoder
)Encoder
feignEncoder:SpringEncoder
Logger
feignLogger:Slf4jLogger
Contract
feignContract:SpringMvcContract
Feign.Builder
feignBuilder:HystrixFeign.Builder
Client
feignClient:若是啓用了Ribbon,則它是LoadBalancerFeignClient
,不然使用默認的feign客戶端。能夠經過將feign.okhttp.enabled
或feign.httpclient.enabled
分別設置爲true
,並將它們放在類路徑上來使用OkHttpClient
和ApacheHttpClient
feign客戶端,你能夠經過在使用Apache時提供ClosableHttpClient
或在使用OK HTTP時提供OkHttpClient
的bean來定製使用的HTTP客戶端。
Spring Cloud Netflix默認狀況下不爲feign提供如下bean,但仍然從應用程序上下文中查找這些類型的bean以建立feign客戶端:
Logger.Level
Retryer
ErrorDecoder
Request.Options
Collection<RequestInterceptor>
SetterFactory
建立其中一種類型的bean並將其放在@FeignClient
配置中(如上面的FooConfiguration
)容許你覆蓋所描述的每一個bean,例如:
@Configuration public class FooConfiguration { @Bean public Contract feignContract() { return new feign.Contract.Default(); } @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("user", "password"); } }
這將使用feign.Contract.Default
替換SpringMvcContract
,並將RequestInterceptor
添加到RequestInterceptor
的集合中。
@FeignClient
也可使用配置屬性進行配置。
application.yml
feign: client: config: feignName: connectTimeout: 5000 readTimeout: 5000 loggerLevel: full errorDecoder: com.example.SimpleErrorDecoder retryer: com.example.SimpleRetryer requestInterceptors: - com.example.FooRequestInterceptor - com.example.BarRequestInterceptor decode404: false encoder: com.example.SimpleEncoder decoder: com.example.SimpleDecoder contract: com.example.SimpleContract
能夠以與上述相似的方式在@EnableFeignClients
屬性defaultConfiguration
中指定默認配置,不一樣之處在於此配置將適用於全部feign客戶端。
若是你更喜歡使用配置屬性來配置全部@FeignClient
,則可使用default
feign名稱建立配置屬性。
application.yml
feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic
若是咱們同時建立@Configuration
bean和配置屬性,配置屬性將獲勝,它將覆蓋@Configuration
值,可是,若是要將優先級更改成@Configuration
,則能夠將feign.client.default-to-properties
更改成false
。
若是須要在RequestInterceptor
中使用ThreadLocal
綁定變量,則須要將Hystrix的線程隔離策略設置爲「SEMAPHORE」或在Feign中禁用Hystrix。
application.yml
# To disable Hystrix in Feign feign: hystrix: enabled: false # To set thread isolation to SEMAPHORE hystrix: command: default: execution: isolation: strategy: SEMAPHORE
若是咱們想要建立具備相同名稱或URL的多個feign客戶端,以便它們指向同一服務器但每一個都具備不一樣的自定義配置,那麼咱們必須使用@FeignClient
的contextId
屬性,以免這些配置bean的名稱衝突。
@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class) public interface FooClient { //.. }
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class) public interface BarClient { //.. }
在某些狀況下,可能須要以使用上述方法沒法實現的方式自定義Feign客戶端,在這種狀況下,你可使用Feign Builder API建立客戶端。下面是一個示例,它建立兩個具備相同接口的Feign客戶端,但使用單獨的請求攔截器配置每一個客戶端。
@Import(FeignClientsConfiguration.class) class FooController { private FooClient fooClient; private FooClient adminClient; @Autowired public FooController(Decoder decoder, Encoder encoder, Client client, Contract contract) { this.fooClient = Feign.builder().client(client) .encoder(encoder) .decoder(decoder) .contract(contract) .requestInterceptor(new BasicAuthRequestInterceptor("user", "user")) .target(FooClient.class, "http://PROD-SVC"); this.adminClient = Feign.builder().client(client) .encoder(encoder) .decoder(decoder) .contract(contract) .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin")) .target(FooClient.class, "http://PROD-SVC"); } }
在上面的示例中,
FeignClientsConfiguration.class
是Spring Cloud Netflix提供的默認配置。
PROD-SVC
是客戶端將向其發出請求的服務的名稱。
FeignContract
對象定義了哪些註解和值在接口上是有效的,自動裝配的Contract
bean提供對SpringMVC註解的支持,而不是默認的Feign原生註解。
若是Hystrix位於類路徑而且feign.hystrix.enabled=true
,則Feign將使用斷路器包裝全部方法,返回com.netflix.hystrix.HystrixCommand
也可用,這容許你使用反應模式(經過調用.toObservable()
或.observe()
或異步使用(經過調用.queue()
)。
要在每一個客戶端的基礎上禁用Hystrix支持,請建立一個帶有「prototype」範圍的vanilla Feign.Builder
,例如:
@Configuration public class FooConfiguration { @Bean @Scope("prototype") public Feign.Builder feignBuilder() { return Feign.builder(); } }
在Spring Cloud Dalston發佈以前,若是Hystrix在類路徑上,Feign會默認將全部方法包裝在斷路器中,Spring Cloud Dalston中更改了此默認行爲,轉而採用了選擇加入方法。
Hystrix支持回退的概念:在電路打開或出現錯誤時執行的默認代碼路徑,要爲給定的@FeignClient
啓用回退,請將fallback
屬性設置爲實現回退的類名,你還須要將實現聲明爲Spring bean。
@FeignClient(name = "hello", fallback = HystrixClientFallback.class) protected interface HystrixClient { @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello iFailSometimes(); } static class HystrixClientFallback implements HystrixClient { @Override public Hello iFailSometimes() { return new Hello("fallback"); } }
若是須要訪問產生回退觸發器的緣由,可使用@FeignClient
中的fallbackFactory
屬性。
@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class) protected interface HystrixClient { @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello iFailSometimes(); } @Component static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> { @Override public HystrixClient create(Throwable cause) { return new HystrixClient() { @Override public Hello iFailSometimes() { return new Hello("fallback; reason was: " + cause.getMessage()); } }; } }
在Feign中實現回退以及Hystrix回退如何工做都有必定的限制,返回com.netflix.hystrix.HystrixCommand
和rx.Observable
的方法目前不支持回退。
@Primary
當與Hystrix回退一塊兒使用Feign時,ApplicationContext
中有相同類型的多個bean,這將致使@Autowired
沒法工做,由於沒有一個明確的bean或一個標記爲primary
的bean。爲了解決這個問題,Spring Cloud Netflix將全部Feign實例標記爲@Primary
,所以Spring Framework將知道要注入哪一個bean,在某些狀況下,這可能並不理想,要關閉此行爲,請將@FeignClient
的primary
屬性設置爲false
。
@FeignClient(name = "hello", primary = false) public interface HelloClient { // methods here }
Feign經過單繼承接口支持樣板api,這容許將通用操做分組爲方便的基本接口。
UserService.java
public interface UserService { @RequestMapping(method = RequestMethod.GET, value ="/users/{id}") User getUser(@PathVariable("id") long id); }
UserResource.java
@RestController public class UserResource implements UserService { }
UserClient.java
package project.user; @FeignClient("users") public interface UserClient extends UserService { }
一般不建議在服務器和客戶端之間共享接口,它引入了緊耦合,而且實際上也不能以其當前形式使用Spring MVC(方法參數映射不會被繼承)。
你能夠考慮爲你的Feign請求啓用請求或響應GZIP壓縮,你能夠經過啓用如下屬性之一來執行此操做:
feign.compression.request.enabled=true feign.compression.response.enabled=true
Feign請求壓縮爲你提供相似於你爲Web服務器設置的設置:
feign.compression.request.enabled=true feign.compression.request.mime-types=text/xml,application/xml,application/json feign.compression.request.min-request-size=2048
經過這些屬性,你能夠選擇壓縮介質類型和最小請求閾值長度。
爲每一個建立的Feign客戶端建立一個記錄器,默認狀況下,記錄器的名稱是用於建立Feign客戶端的接口的完整類名,Feign日誌記錄僅響應DEBUG
級別。
application.yml
logging.level.project.user.UserClient: DEBUG
你能夠爲每一個客戶端配置Logger.Level
對象,告訴Feign要記錄多少,選擇是:
NONE
,沒有記錄(DEFAULT)。BASIC
,僅記錄請求方法和URL以及響應狀態代碼和執行時間。HEADERS
,記錄基本信息以及請求和響應headers。FULL
,記錄請求和響應的headers、body和元數據。例如,如下內容將Logger.Level
設置爲FULL
:
@Configuration public class FooConfiguration { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
OpenFeign @QueryMap
註解爲POJO提供了支持,可用做GET參數映射,不幸的是,默認的OpenFeign QueryMap註解與Spring不兼容,由於它缺乏value
屬性。
Spring Cloud OpenFeign提供等效的@SpringQueryMap
註解,用於將POJO或Map參數註解爲查詢參數映射。
例如,Params
類定義參數param1
和param2
:
// Params.java public class Params { private String param1; private String param2; // [Getters and setters omitted for brevity] }
如下feign客戶端使用@SpringQueryMap
註解使用Params
類:
@FeignClient("demo") public class DemoTemplate { @GetMapping(path = "/demo") String demoEndpoint(@SpringQueryMap Params params); }