天天看點

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<Store> 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<HystrixClient> {
    @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;
    }
}