不用找了,10分鐘幫你搞定 feign+spring cloud!看完秒懂
- 2020 年 3 月 13 日
- 筆記
轉載自:https://www.cnblogs.com/roytian/p/12196790.html
什麼是feign?
來自官網的解釋:Feign makes writing java http clients easier
https://github.com/OpenFeign/feign
在使用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》。