1. 程式人生 > >Feign:web service client(譯)

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