不用找了,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》。