Feign是一种声明式的web service client。它让web service变得更容易。使用Feign你只需要创建一个接口并且写上注解。它提供插拔式的Feign注解和JAX-RS注解支持。Feign同样提供插拔式的编码解码器。Spring Cloud添加了Spring MVC的注解支持,在Spring web中默认使用相同的HttpMessageConverters。spring cloud集成了Ribbon 和 Eureka去提供负载均衡。
org.springframework.cloud and artifact id spring-cloud-starter-feign。Spring Cloud Project page。
Example spring boot app:
@Configuration
@ComponentScan
@EnableAutoConfiguration
@EnableEurekaClient
@EnableFeignClients
public class Application {
public stati void main(String[] args){
SpringApplication.run(Application.class,args);
}
}
StoreClient.java
@FeignClient("sotes")
public interface StoreClient{
@RequestMapping(method=RequestMethod.GET,value="/stores")
List getStores();
@RequestMapping(method=RequestMethod.POST,value="/stores/{storeId}",consumes="appliation/json")
Store update(@PathVariable("storeId") Long storeId,Store store);
}
在@FeignClient注解里是一个任意的服务端的名字(比如 “store”),用于创建一个Ribbon负载均衡。你也可以指定一个URL,通过使用url属性(绝对值或者只是个hostname)。应用程序上下文中的bean的名称是接口的完全限定名称。一个别名同样被创建就是 “name”属性上附加上“FeignClient”。看上面的列子,@Qualifire(“storesFeignClient”)可以用来引用bean,如果你想改变默认@Qualifier值,这可以在@FeignClient使用qualifier值。
Ribbon client会发现“stores”服务的物理地址。如果你的应用是Eureka client然后Eureka注册中心会决定service的地址。如果你不想使用Eureka,你可以简单的配置一个 server list 在你的外配配置中。
Sping cloud Feign支持的一个核心概念就是声明的客户端。每一个Feign client是整体的的一部分一起通过远程服务器联系,使用@FeignClient注解指定一个整体使用的名字。Sping cloud为每一个使用FeignClientConfiguration声明的客户端创建一个新的ApplictionContxt。这包括(除去其他东西)feign.Decode,feign.Encoder和feign.Contract。
Spring cloud提供通过@FeignClient.添加添额外的配置的方法让你完全控制feign client。例如:
@FeignClient(name="stores", configuration=FooConfiguration.class)
public interface StoreClient{
}
在这个例子中,FeignClientsConfiguration已经有的和FooConfiguration自定义的共同组成了client(后者会覆盖先者)。
警告: FooConfiguration必须是@Configuration,但是注意不能在@CompinentScan中,否则将被用于每个@FeignClient。如果你使用@ComponentScan(或@ SpringBootApplication),你需要采取一些措施来避免它被列入(比如把它放在一个单独的,非重叠的包,或者指定包在@ComponentScan明确扫描)。
注意:该 serviceId 已经过时,建议使用 name 属性
警告:以前,使用 url 属性,则 name 不是必须的,但现在是必须的.
name 和 url 属性都支持占位符。
@FeignClient(name="${feign,name}",url="${feign.url}")
public interface StoreClient{
}
Spring cloud netflix默认给feign提供如下bean(BeanType beanName:ClassName)
* Decoder feignDecoder: RespinseEntityDecoder(包装了SpringDeccoder)
* Encoder fergnEncoder: SpringEncoder
* Logger feignLogger: SLF4JLogger
* contract feignContract:SpringMvcContract
* Feign.Builder feignBuilder: HystrixFeign.Builder
* Client feignClient:如果Ribbon可用就是loadBalancerFeignClient,否则默认feign client。
OkHttpClient和ApacheHttpClient feign clients可以通过分别设置fiegn.okhttp.enable 或者 feign.httpclient.enable为true,并且添加到classpath。
Spring cloud netflix默认没有提供一下bean,但是仍然可以从上下文中查找这些bean并创建feign client:
* Logger.Level
* Retryer
* ErrorDecoder
* Request.options
* Collection
创建这些类型的一个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以同样的方式被指定。不同之处是配置会加载到所有的feign clients。
在一些情况下可能需要自定义Feign clients但是不能用以上的方法。所以你可以使用Feign Builder API创建clients。下面是一个例子,创建了两个相同接口的client但是用配置了分开的拦截器。
@Import(FeignClientsConfiguration.class)
class FooController {
private FooClient fooClient;
private FooClient adminClient;
@Autowired
public FooController(
ResponseEntityDecoder decoder, SpringEncoder 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是我们提供的服务名称,会接收相应的客户端的请求。
如果Hystrix在classpath中,默认Feign用熔断器包装所有方法。返回一个 com.netflix.hystrix.HystrixCommand 也是可用的。这允许你以被动模式使用(使用.toObservable()或者.observer())或者 异步调用(.queue())。
要禁用Feign 的 Hystrix支持,设置feign.hystrix.enabled=false.
要在每个客户端上禁用 Hystrix 支持,创建一个 Feign.Builder 并将scope 设置为”prototype”,例如:
@Configuration
public class FooConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
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");
}
}
如果一个请求需要触发回退,可以使用fallbackFactory属性替换@FeignClient。
@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello iFailSometimes();
}
@Component
static class HystrixClientFallbackFactory implements FallbackFactory {
@Override
public HystrixClient create(Throwable cause) {
return new HystrixClientWithFallBackFactory() {
@Override
public Hello iFailSometimes() {
return new Hello("fallback; reason was: " + cause.getMessage());
}
};
}
}
警告:There is a limitation with the implementation of fallbacks in Feign and how Hystrix fallbacks work. Fallbacks are currently not supported for methods that return com.netflix.hystrix.HystrixCommand and rx.Observable.
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 {
}
注意:通常在一个server和一个client之间共享一个接口是不可取的。它引入了紧耦合,实际上它也不会spring mvc中起作用(方法参数映射不会被继承)。
你可能考虑对你的Feign请求启用GZIP压缩。你可以通过设置如下启用:
feign.compression.request.enabled=true
feign.compression.response.enabled=true
Feign提供的压缩设置与你的Web server的设置类似:
feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=2048
这些属性允许你选择要压缩的 MIME-TYPE 和最小的请求长度。
每个Feign client都创建了一个logger。默认的logger的命名是Feign client的全限定名。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;
}
}