feign架構原理解析


什么是feign?

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

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

拿okhttp舉例:

    public static void post(String url, HashMap<String, 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,String> paramsMap = new HashMap<String, String>() ;
        paramsMap.put("name","小明");
        paramsMap.put("html","<html>...");
        post("https://10.0.4.147:8015/crcc",paramsMap);
    }

 

有了feign之后我們怎么發送請求

@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);
}
@Autowired
FooBarService fooBarService;
public String foo() {
    return fooBarService.foo("rt");
}

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

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

 

spring cloud openfeign的加載過程

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

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

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

  

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

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

feign的REST Client API思想

JAX-RS標准

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

JAX-RS2.0 之 REST Client API

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

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

jersey

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

jersey 之 REST Client API

官方文檔示例

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

 

feign與JAX-RS2.0

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

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

feign中怎么進行對應呢?

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

 

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

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

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

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

 

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

  

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的配置

配置的優先級順序

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

 

properties和spring bean可以配置的內容

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

 

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

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

這里的FeignContext跟ribbon的SpringClientFactory同理 

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

 

feign與ribbon對接的關鍵點

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM