Feign:web service client(译)

Declarative REST Client:Feign

Feign是一种声明式的web service client。它让web service变得更容易。使用Feign你只需要创建一个接口并且写上注解。它提供插拔式的Feign注解和JAX-RS注解支持。Feign同样提供插拔式的编码解码器。Spring Cloud添加了Spring MVC的注解支持,在Spring web中默认使用相同的HttpMessageConverters。spring cloud集成了Ribbon 和 Eureka去提供负载均衡。

How to include Feign

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 在你的外配配置中。

Overriding Feign Defaults

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。

Creating Feign Clients Manually

在一些情况下可能需要自定义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是我们提供的服务名称,会接收相应的客户端的请求。

Feign Hystrix Support

如果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();
    }
}

Feign Hystrix Fallbacks

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 Inheritance Support

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 request/response compression

你可能考虑对你的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 logging

每个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;
    }
}

你可能感兴趣的:(JAVA)