什麼是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!看完秒懂](http://p2.ttnews.xyz/loading.gif)
<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!看完秒懂](http://p2.ttnews.xyz/loading.gif)
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
<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中怎麼進行對應呢?
為什麼這麼去抽象我們這裡也暫不深入研究(更深層的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同理
<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模板的實現類,這樣就可以對執行請求前後做一些負載邏輯。
閱讀更多 Java高級架構師l 的文章