不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

什麼是feign?

來自官方的解釋:Feign makes writing java http clients easier

<code>https://github.com/OpenFeign/feign/<code>

在使用feign之前,我們怎麼發送請求?

拿okhttp舉例:

<code>public static void post(String url, HashMap<string> paramsMap){
        OkHttpClient mOkHttpClient = new OkHttpClient();
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        Set<string> keySet = paramsMap.keySet();
        for(String key:keySet) {
            String value = paramsMap.get(key);
            formBodyBuilder.add(key,value);
        }
        FormBody formBody = formBodyBuilder.build();
        Request request = new Request
                .Builder()
                .post(formBody)
                .url(url)
                .build();
        try (Response response = mOkHttpClient.newCall(request).execute()) {
            System.out.println(response.body().string());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HashMap<string> paramsMap = new HashMap<string>() ;
        paramsMap.put("name","小明");
        paramsMap.put("html","...");
        post("https://10.0.4.147:8015/crcc",paramsMap);
    }/<string>/<string>/<string>/<string>/<code>

有了feign之後我們怎麼發送請求

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

<code>@FeignClient(value = "FooBarService", configuration = FooBarServiceFeignConfiguration.class)
public interface FooBarService {
    @RequestMapping(value = "/foo", method = RequestMethod.GET)
    String foo(@RequestParam(value = "param1") String param1);

    @RequestMapping(value = "/bar", method = RequestMethod.POST)
    String bar(@RequestParam(value = "param1") String param1, @RequestParam(value = "param2") String param2);
}/<code>
<code>@Autowired
FooBarService fooBarService;
public String foo() {
    return fooBarService.foo("rt");
}/<code>


幾行代碼就能搞定,很大程度的節省了工作量,而且客戶端和服務端關於接口的定義只需要寫一次

具體的利弊我們這裡就不做分析,在微服務盛行的現在,服務之間的調用單純使用http client的場景已經基本不存在

spring cloud openfeign的加載過程

上面的代碼為什麼接口沒有實現類也可以使用,是不是跟mybatis一樣使用了代理?

沒錯,接口最後都會生成代理實現

(右鍵新標籤打開可查看大圖)

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

spring cloud openfeign關於代理的生成過程

(右鍵新標籤打開可看大圖)

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

feign的REST Client API思想

JAX-RS標準

最新的REST接口標準為JAX-RS2.0,但是標準是供參考不能拿來直接吃的,具體還是要通過實現了標準的中間件來進行使用

JAX-RS2.0 之 REST Client API

摘自《Java RESTful Web Service實戰(第2版)》

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

為什麼JAX-RS2.0這麼去抽象,我們這裡暫不深入去思考,先拿來主義

jersey

jersey是JAX-RS標準的參考實現,是Java領域中最純正的REST服務開發框架,例如eureka也是使用jersey來做REST接口和客戶端發送請求,詳見《服務發現之eureka》

jersey 之 REST Client API

<code>ClientConfig clientConfig = new ClientConfig();
clientConfig.register(MyClientResponseFilter.class);
clientConfig.register(new AnotherClientFilter());
 
Client client = ClientBuilder.newClient(clientConfig);
client.register(ThirdClientFilter.class);
 
WebTarget webTarget = client.target("http://example.com/rest");
webTarget.register(FilterForExampleCom.class);
WebTarget resourceWebTarget = webTarget.path("resource");
WebTarget helloworldWebTarget = resourceWebTarget.path("helloworld");
WebTarget helloworldWebTargetWithQueryParam =
        helloworldWebTarget.queryParam("greeting", "Hi World!");
 
Invocation.Builder invocationBuilder =
        helloworldWebTargetWithQueryParam.request(MediaType.TEXT_PLAIN_TYPE);
invocationBuilder.header("some-header", "true");
 
Response response = invocationBuilder.get();
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));/<code>

feign與JAX-RS2.0

feign主要是作為客戶端發送請求,所以也是參考對照了JAX-RS2.0標準

feign並不是REST Client,只是參考了REST Client的實現,具體的目標還是為了更簡單的實現http client請求

feign中怎麼進行對應呢?

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

為什麼這麼去抽象我們這裡也暫不深入研究(更深層的JAX-RS為什麼這麼抽象還未探明)

feign代理的執行流程和關鍵對象

代理生成時用到了什麼組件、代理執行時用到了什麼組件?

上文我們知道,所有請求最後都交給了MethodHandler來執行,所以我們重點關注MethodHandler即可

(右鍵新標籤打開可查看大圖)

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

MethodHandler的關鍵對象和執行請求的流程

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

1.RequestTemplate.Factory

創建RequestTemplate的工廠,包含MethodMetadata和Encoder對象

其中MethodMetadata是應用初始化時Contract解析@RequestMapping @RequestParam等註解而來的中間數據

2.

Encoder

報文壓縮gzip等

3.RequestInterceptor

為請求附加一些信息,類似spring mvc的interceptor攔截器

4.Target

主要是把@FeignClient裡的url拼接到RequestTemplate

5.Options

用於請求的參數配置

6.Decoder

解析返回報文,如果返回404,判斷decode404==true則解析,否則交給ErrorDecoder解析

7.ErrorDecoder

請求錯誤處理

8.Logger.Level

日誌等級,包含四種 none basic headers full

9.Logger

對應的配置為LoggerFactory,記錄日誌用

10.Retryer

重試,DefaultRetryer默認會重試5次

11.Client

真正執行http請求的客戶端,可以配置,默認由FeignRibbonClientAutoConfiguration進行配置結合ribbon使用

spring cloud openfeign的配置

配置的優先級順序

(右鍵新標籤打開可查看大圖)

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

properties和spring bean可以配置的內容

主要還是配置我們上面feign的關鍵對象,properties和spring bean可配置的項如下

不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂

同ribbon一樣,spring-cloud-openfeign的配置也是懶加載,每個feignclient都可以有自己個性化的配置,且配置是懶加載的,但是為每個接口生成代理的時候已經去註冊和使用了相關的配置,其實懶加載沒有用了。

所以只實現了最終目的:每個feignclient 都可以有自己個性化的配置

這裡的FeignContext跟ribbon的SpringClientFactory同理

<code>public class FeignContext extends NamedContextFactory<feignclientspecification> {
   public FeignContext() {
      super(FeignClientsConfiguration.class, "feign", "feign.client.name");
   }
}/<feignclientspecification>/<code>

feign與ribbon對接的關鍵點

feign與ribbon對接主要還是在Client對象上做文章,將Client替換為繼承Ribbon模板的實現類,這樣就可以對執行請求前後做一些負載邏輯。


分享到:


相關文章: