spring-cloud-feign源碼深度解析

Feign是一個聲明式的Web服務客戶端。這使得Web服務客戶端的寫入更加方便 要使用Feign建立一個界面並對其進行註釋。它具備可插拔註釋支持,包括Feign註釋和JAX-RS註釋。Feign還支持可插拔編碼器和解碼器。Spring Cloud添加了對Spring MVC註釋的支持,並在Spring Web中使用默認使用的HttpMessageConverters。Spring Cloud集成Ribbon和Eureka以在使用Feign時提供負載均衡的http客戶端。java

在介紹spring cloud feign以前,先來看看原生的feign,而後在看spring cloud feign是怎麼樣集成原生的feign的。git

1、原生的feign的用法github

一、引入jarspring

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-gson</artifactId>
    <version>9.5.0</version>
</dependency>

二、編寫代碼,如下是發送GET和POST請求方式(更多請查看)apache

public class FeignTest {
    
    interface BookService{
        @RequestLine("GET /book/borrow?name={name}&timeout={timeout}")
        String borrow(@Param("name")String name,@Param("timeout")Integer timeout);
        
        @RequestLine("POST /book/post")
        @Headers("Content-Type: application/json")
        @Body("%7B\"id\": \"{id}\", \"name\": \"{name}\"%7D")
        String post(@Param("id")String id,@Param("name")String name);
    }

    public static void main(String[] args) {
        BookService bs = Feign.builder()
                .options(new Options(2000, 6000))
                .target(BookService.class, "http://localhost:2001");
        String result = bs.borrow("2w2w",1);
        System.out.println(result);
        result = bs.post("12345", "spring feign");
        System.out.println(result);
    }
}

feign核心類介紹編程

  • feign.Feign.Builder 設置發送http請求的相關參數,好比http客戶端,重試策略,編解碼,超時時間等等json

    • feign.Contract.Default 解析接口方法的元數據,構建http請求模板
    • feign.Client 發送http請求客戶端,默認實現feign.Client.Default,使用的是java.net包實現的
    • Retryer 重試,默認實現feign.Retryer.Default,超時延遲100ms開始重試,每隔1s重試一次,重試4次
    • Options 超時時間,默認鏈接超時10s,讀超時60s
    • feign.codec.Encoder 編碼器
    • feign.codec.Decoder 解碼器
    • RequestInterceptor 請求攔截器,能夠在發送http請求以前執行此攔截器
    • feign.Contract 接口以及方法元數據解析器
      以上參數均可以本身擴展
  • HardCodedTarget 定於目標接口和url
  • ReflectiveFeign 生成動態代理類,基於jdk的動態代理實現
  • feign.InvocationHandlerFactory.Default 接口方法統一攔截器建立工廠
  • FeignInvocationHandler 接口統一方法攔截器
  • ParseHandlersByName 解析接口方法元數據
  • SynchronousMethodHandler.Factory 接口方法的攔截器建立工廠
  • SynchronousMethodHandler 接口方法的攔截器,真正攔截的核心,這裏真正發起http請求,處理返回結果

圖片描述

在上述示例代碼中,到底feign給我作了哪些事情呢?下面的時序圖爲咱們展現整個處理過程api

圖片描述

一、經過feign.Feign.Builder爲咱們設置http請求的相關參數,好比http客戶端,重試策略,編解碼,超時時間,這裏都是面向接口編程實現的,咱們很容易的進行擴展,好比http客戶端,可使用java原生的實現,也可使用apache httpclient,亦可使用okHttpClient,本身喜歡就好,其餘屬性亦是如此,由此看出feign的設計具備很是好的可擴展性。mvc

二、ReflectiveFeign內部使用了jdk的動態代理爲目標接口生成了一個動態代理類,這裏會生成一個InvocationHandler(jdk動態代理原理)統一的方法攔截器,同時爲接口的每一個方法生成一個SynchronousMethodHandler攔截器,並解析方法上的 元數據,生成一個http請求模板。app

三、當發起方法調用的時候,被統一的方法攔截器FeignInvocationHandler攔截,再根據不一樣的方法委託給不一樣的SynchronousMethodHandler攔截器處理。

四、根據每次方法調用的入參生成http請求模板,若是設置了http請求攔截器,則先經歷攔截器的處理,再發起真正的http請求,獲得結果後會根據方法放入返回值進行反序列化,最後返回給調用方。

五、若是發生了異常,會根據重試策略進行重試。

feign也整合了Hystrix,實現熔斷降級的功能,其實也很簡單,上面的分析咱們知道了feign在方法調用的時候會通過統一方法攔截器FeignInvocationHandler的處理,而HystrixFeign則是使用了HystrixInvocationHandler代替,在方法調用的時候進行Hystrix的封裝,這裏須要特別說明下:

  • Hystrix有超時時間,feign自己也有超時時間,正常來講Hystrix的超時間要大於feign的超時時間,若是是小於的話,Hytrix已經超時了,feign再等待就已經沒有意義了。
  • 再則就是feign超時的話會觸發重試操做,此時要是Hytrix發生超時異常返回了,但這並不會切斷feign的繼續操做,什麼意思呢?假設Hytrix的超時時間爲1s,feign設置的超時時間爲2s,而真正業務操做須要耗時3s,這時Hytrix超時異常返回,然後feign也會發生超時異常,可是feign會根據超時策略繼續進行重試操做,並不會由於Hytrix的中斷而中斷。因此Hytrix的超時時間通常要大於feign的總超時時間,如這個例子中要設置2 5(默認重試次數4 + 1)=10s,公式就是Hytrix的超時間=feign的超時 時間 (feign的重試次數 + 1)

以上就是原生feign的基本原理,下面咱們來分析下springcloud是如何進行整合進來的,又添加了一些什麼東西?

spring-cloud-feign的基本用法:

一、開啓註解EnableFeignClients

@EnableFeignClients
public class ApplicationStartup {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationStartup.class, args);
    } 
}

二、定義接口

@FeignClient(name = "user")
public interface IUserService{
    @RequestMapping(value = "/api/getUser")
    User getUser(Long id);
}

三、這樣經過spring注入IUserService就可使用了

@Controller
public class UserController {
    @Autowired
    private IUserService userService;
}

使用起來很是簡單,核心的兩步就是開啓註解,定於feignclient接口。

1、開啓@EnableFeignClients註解到底給咱們作了什麼事情呢?

一、掃描EnableFeignClients註解上的配置信息,註冊默認的配置類,這個配置類是對全部feignclient的都是生效的,即爲全局的配置。

二、掃描帶有@FeignClient註解的接口,並註冊配置類(此時的配置類針對當前feignclient生效)和FeignClientFactoryBean,此bean實現了FactoryBean接口,咱們知道spring有兩種類型的bean對象,一種是普通的bean,另外一種則是工廠bean(FactoryBean),它返回的實際上是getObject方法返回的對象(更多關於FactoryBean的相關信息請查看spring官方文檔)。getObject方法就是集成原生feign的核心方法,當spring注入feignclient接口時,getObject方法會被調用,獲得接口的代理類。

  • 備註:在FeignClient指定配置類時,切記不要被spring容器掃描到,否則會對全局生效。

2、自動加載配置類FeignAutoConfiguration,FeignClientsConfiguration,FeignRibbonClientAutoConfiguration,這三個類爲feign提供了全部的配置類,默認狀況下所加載的類狀況:

  • feign.Feign.Builder 當引入了Hytrix並開啓參數feign.hystrix.enabled=true後,則會加載feign.hystrix.HystrixFeign.Builder,此時feign就具有降級熔斷的功能了。
  • feign.Client 此實現類的加載分兩種狀況:

    • 使用url方式:feign.Client.Default,使用java原生的方式(java.net包)發起http請求,也能夠本身擴展。
    • 使用name方式:LoadBalancerFeignClient,集成了ribbon,實現服務發現與負載均衡,可是真正發起http請求仍是java原生的方式
      此處是一擴展點,當咱們引入ApacheHttpClient時,http客戶端就會使用apache的httpClient;當咱們引入OkHttpClient時,http客戶端就會使用okhttp3.OkHttpClient。
  • feign.Retryer 默認Retryer.NEVER_RETRY,不進行重試,這裏也能夠本身實現Retryer接口實現本身的重試策略,可是feign在集成了ribbon的狀況下,最好保持默認不進行重試,由於ribbon也會有重試策略,若是feign也開啓重試,容易產生混亂;其實在低版本中spring-cloud-feing重試默認並非NEVER_RETRY,可能spring-cloud-feing也意識到這樣作的問題,因此在D版中改爲NEVER_RETRY了。
  • feign.Request.Options 默認設置鏈接超時時間是10,讀超時時間是60s。這裏也能夠更改,分兩種狀況:

    • 使用url方式:必須經過這個參數來設置,才生效

      @Configuration
      public class MyConfig {
          @Bean
          public Request.Options options(){
              Request.Options o = new Options(1000, 1000);
              return o;
          }
      }

      而後在註解上@FeignClient指定:

      @FeignClient(name="",url="",configuration= {MyConfig.class})

      注意此類不能被spring容器掃描到,不然會對全局生效。你也能夠經過註解@EnableFeignClients來全局指定:

      @EnableFeignClients(defaultConfiguration=MyConfig.class)
    • 使用name方式:此時已經集成了ribbon,可使用如下配置來設置,若是你此時也配置了Options,如下配置會被覆蓋

      # 對全部的feignclient生效
      ribbon.ReadTimeout=10000
      ribbon.ConnectTimeout=2000
      
      # 對指定的feignclien生效
      [feignclientName].ribbon.ReadTimeout=10000
      [feignclientName].ribbon.ConnectTimeout=2000

      若是開啓Hytrix,hytrix也有超時時間設置,可是hytrix是封裝在feign基礎之上的,上文已有分析。

      hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000

      你也能夠關閉hytrix的超時時間

      hystrix.command.default.execution.timeout.enabled=false
  • feign.codec.Decoder 解碼器,默認使用了HttpMessageConverters來實現
  • feign.codec.Encoder 編碼器,默認使用了HttpMessageConverters來實現
  • feign.Contract 默認提供springmvc的註解解析,支持@RequestMapping,@RequestBody,@RequestParam,@PathVariable

最後三種也是spring-cloud-feign替換原生feign的默認實現,對springMVC的相關支持,會另有文章進行解析。

spring-cloud-feign的核心源碼解析到此結束了,不知道是否對您有無幫助,可留言跟我交流。

相關文章
相關標籤/搜索