Feign是一個聲明式的Web服務客戶端。這使得Web服務客戶端的寫入更加方便 要使用Feign創建一個界面并對其進行注釋。它具有可插入注釋支持,包括Feign注釋和JAX-RS注釋。Feign還支持可插拔編碼器和解碼器。Spring Cloud增加了對Spring MVC注釋的支持,并使用Spring Web中默認使用的HttpMessageConverters。Spring Cloud集成Ribbon和Eureka以在使用Feign時提供負載均衡的http客戶端。
如何加入Feign
要在您的項目中包含Feign,請使用組org.springframework.cloud和工件IDspring-cloud-starter-feign的啟動器。有關使用當前的Spring Cloud發布列表設置構建系統的詳細信息,請參閱Spring Cloud項目頁面。
示例spring boot應用
@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableEurekaClient
@EnableFeignClients
public class Application {
? ? public static void main(String[] args) {
? ? ? ? SpringApplication.run(Application.class, args);
? ? }
}
StoreClient.java
@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值(以上“存儲”)是一個任意的客戶端名稱,用于創建Ribbon負載平衡器(有關Ribbon支持的詳細信息,請參閱下文))。您還可以使用url屬性(絕對值或只是主機名)指定URL。應用程序上下文中的bean的名稱是該接口的完全限定名稱。要指定您自己的別名值,您可以使用@FeignClient注釋的qualifier值。
以上的Ribbon客戶端將會發現“商店”服務的物理地址。如果您的應用程序是Eureka客戶端,那么它將解析Eureka服務注冊表中的服務。如果您不想使用Eureka,您可以簡單地配置外部配置中的服務器列表(例如,參見
上文)。
覆蓋Feign默認值
Spring Cloud的Feign支持的中心概念是指定的客戶端。每個假裝客戶端都是組合的組件的一部分,它們一起工作以根據需要聯系遠程服務器,并且該集合具有您將其作為應用程序開發人員使用@FeignClient注釋的名稱。Spring Cloud根據需要,使用FeignClientsConfiguration為每個已命名的客戶端創建一個新的集合ApplicationContext。這包含(除其他外)feign.Decoder,feign.Encoder和feign.Contract。
Spring Cloud可以通過使用@FeignClient聲明額外的配置(FeignClientsConfiguration)來完全控制假客戶端。例:
@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屬性。
警告以前,使用url屬性,不需要name屬性。現在需要使用name。
name和url屬性支持占位符。
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
? ? //..
}
Spring Cloud Netflix默認為feign(BeanTypebeanName:ClassName)提供以下bean:
DecoderfeignDecoder:ResponseEntityDecoder(其中包含SpringDecoder)
EncoderfeignEncoder:SpringEncoder
LoggerfeignLogger:Slf4jLogger
ContractfeignContract:SpringMvcContract
Feign.BuilderfeignBuilder:HystrixFeign.Builder
ClientfeignClient:如果Ribbon啟用,則為LoadBalancerFeignClient,否則將使用默認的feign客戶端。
可以通過將feign.okhttp.enabled或feign.httpclient.enabled設置為true,并將它們放在類路徑上來使用OkHttpClient和ApacheHttpClient feign客戶端。
Spring Cloud Netflix 默認情況下不提供以下bean,但是仍然從應用程序上下文中查找這些類型的bean以創建假客戶機:
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");
? ? }
}
這將SpringMvcContract替換為feign.Contract.Default,并將RequestInterceptor添加到RequestInterceptor的集合中。
可以在@EnableFeignClients屬性defaultConfiguration中以與上述相似的方式指定默認配置。不同之處在于,此配置將適用于所有假客戶端。
注意如果您需要在RequestInterceptor`s you will need to either set the
thread isolation strategy for Hystrix to `SEMAPHORE中使用ThreadLocal綁定變量,或在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
手動創建Feign客戶端
在某些情況下,可能需要以上述方法不可能自定義您的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) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
.target(FooClient.class, "http://PROD-SVC");
this.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
.target(FooClient.class, "http://PROD-SVC");
? ? }
}
注意在上面的例子中,FeignClientsConfiguration.class是Spring Cloud Netflix提供的默認配置。
注意PROD-SVC是客戶端將要求的服務的名稱。
Feign Hystrix支持
如果Hystrix在類路徑上,feign.hystrix.enabled=true,Feign將用斷路器包裝所有方法。還可以返回com.netflix.hystrix.HystrixCommand。這樣就可以使用無效模式(調用.toObservable()或.observe()或異步使用(調用.queue()))。
要在每個客戶端基礎上禁用Hystrix支持創建一個帶有“原型”范圍的香草Feign.Builder,例如:
@Configuration
public class FooConfiguration {
? ? @Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
警告在Spring Cloud達爾斯頓發布之前,如果Hystrix在類路徑Feign中默認將所有方法包裝在斷路器中。這種默認行為在Spring Cloud達爾斯頓改變了贊成選擇加入的方式。
Feign Hystrix回退
Hystrix支持回退的概念:當電路斷開或出現錯誤時執行的默認代碼路徑。要為給定的@FeignClient啟用回退,請將fallback屬性設置為實現回退的類名。
@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 HystrixClientWithFallBackFactory() {
@Override
public Hello iFailSometimes() {
return new Hello("fallback; reason was: " + cause.getMessage());
}
};
}
}
警告在Feign中執行回退以及Hystrix回退的工作方式存在局限性。當前返回com.netflix.hystrix.HystrixCommand和rx.Observable的方法目前不支持回退。
Feign和@Primary
當使用Feign與Hystrix回退時,在同一類型的ApplicationContext中有多個bean。這將導致@Autowired不起作用,因為沒有一個bean,或者標記為主。要解決這個問題,Spring Cloud Netflix將所有Feign實例標記為@Primary,所以Spring Framework將知道要注入哪個bean。在某些情況下,這可能是不可取的。要關閉此行為,將@FeignClient的primary屬性設置為false。
@FeignClient(name = "hello", primary = false)
public interface HelloClient {
// methods here
}
Feign繼承支持
Feign通過單繼承接口支持樣板apis。這樣就可以將常用操作分成方便的基本界面。
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請求/響應壓縮
您可以考慮為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客戶端的接口的完整類名。Feign日志記錄僅響應DEBUG級別。
application.yml
logging.level.project.user.UserClient: DEBUG
您可以為每個客戶端配置的Logger.Level對象告訴Feign記錄多少。選擇是:
NONE,無記錄(DEFAULT)。
BASIC,只記錄請求方法和URL以及響應狀態代碼和執行時間。
HEADERS,記錄基本信息以及請求和響應標頭。
FULL,記錄請求和響應的頭文件,正文和元數據。
例如,以下將Logger.Level設置為FULL:
@Configuration
public class FooConfiguration {
? ? @Bean
? ? Logger.Level feignLoggerLevel() {
? ? ? ? return Logger.Level.FULL;
? ? }
}