②SpringCloud 實戰:引入Feign組件,發起服務間調用

這是SpringCloud實戰系列中第二篇文章,瞭解前面第一篇文章更有助於更好理解本文內容:html

①SpringCloud 實戰:引入Eureka組件,完善服務治理java

簡介

Feign 是一個聲明式的 REST 客戶端,它的目的就是讓 REST 調用更加簡單。git

Feign 提供了 HTTP 請求的模板,經過編寫簡單的接口和插入註解,就能夠定義好 HTTP 請求的參數、格式、地址等信息。github

並且 Feign 會徹底代理 HTTP 請求,咱們只須要像調用方法同樣調用它就能夠完成服務請求及相關處理。Spring Cloud 對 Feign 進行了封裝,使其支持 SpringMVC 標準註解和 ttpMessageConverters。Feign 能夠與 Eureka 和 Ribbon 組合使用以支持負載均衡,與 Hystrix 組合使用,支持熔斷回退。web

若是你沒有使用 Spring Cloud,那麼能夠直接用原生的 Feign 來調用 API,若是你使用了 Spring Cloud,能夠直接用 Spring Cloud OpenFeign 來調用 API。spring

使用原生API

這裏以官方給出的Github示例爲例,展現怎麼使用原生的API來發起請求express

interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);

  @RequestLine("POST /repos/{owner}/{repo}/issues")
  void createIssue(Issue issue, @Param("owner") String owner, @Param("repo") String repo);
}

public static class Contributor {
  String login;
  int contributions;
}

public class MyApp {
  public static void main(String... args) {
    GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");
    // 調用接口,接收返回參數
    List<Contributor> contributors = github.contributors("OpenFeign", "feign");
    for (Contributor contributor : contributors) {
      // 打印輸出結果
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}

上面的代碼是一個 GET 請求的示列,定義了一個 GitHub 的接口,接口中定義了一個查詢的方法和建立Issue的方法。json

在方法上使用了@RequestLine 註解,定義了請求方法類型和請求的 URI,URI 中有對應的參數佔位符,返回值有集合,集合中是對應的結構對象。api

最後經過 Feign 的 builder 模式構建了 GitHub 接口對象後,就能夠直接經過 GiuHub 接口對象調用裏面的 contributors 方法。app

支持的註解

  1. @RequestLine

    做用與方法上;定義請求,支持用大括號{expression}包裝對應@Param註釋參數。

    使用示例:

    @RequestLine("GET /repos/{owner}/{repo}/contributors")
    List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
  2. @Param
    做用於參數上;定義模板變量參數映射,代碼示例同上。

  3. @Headers
    做用於類上或者方法上;定義請求頭Header,代碼示例:

    @Headers("Accept: application/json")
    interface BaseApi<V> {
      @Headers("Content-Type: {contentType}")
      @RequestLine("PUT /api/{key}")
      void put(@Param("key") String key, V value,@Param("contentType") String type);
    }
  4. @QueryMap
    做用於參數上;定義name-value對的映射(POJO),以展開爲查詢字符串,代碼示例:

    public interface Api {
      @RequestLine("GET /find")
      V find(@QueryMap Map<String, Object> queryMap);
    	@RequestLine("GET /findObj")
      V findObj(@QueryMap CustomPojo customPojo);
    }
  5. @HeaderMap
    做用於參數上;映射成HeaderMap,代碼示例:

    public interface Api {
       @RequestLine("POST /")
       void post(@HeaderMap Map<String, Object> headerMap);
    }
  6. @Body
    做用於參數上;定義一個模版,定義一個模版,解析對應的表達式,代碼實例:

    @RequestLine("POST /")
    @Headers("Content-Type: application/xml")
    @Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>")
    void xml(@Param("user_name") String user, @Param("password") String password);
    
    @RequestLine("POST /")
    @Headers("Content-Type: application/json")
    // json curly braces must be escaped!
    @Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
    void json(@Param("user_name") String user, @Param("password") String password);

使用OpenFeign

原生的Feign API 使用已經很方便了,可是還有更簡單的,驚不驚喜意不意外?Spring Cloud 推出了spring-cloud-openfeign,使用OpenFeign比使用原生的API還要簡單。

先建立一個提供服務的項目:eureka-provider

  1. 具體的步驟和上一篇文章建立Eureka-Client 如出一轍,有變更的配置:

    server.port = 8082
    spring.application.name=eureka-provider
    eureka.instance.appname=eureka-provider
  2. 編寫提供服務接口

    @Controller
    public class HelloController{
        @ResponseBody
        @RequestMapping(method = RequestMethod.GET, path = "hello")
        public String hello(){
            return "hello, my name is eureka provider!";
        }
    }
  3. 啓動服務,觀察provider成功註冊到註冊中心

咱們把以前的Eureka-Client 做爲消費者,使用OpenFeign來調用剛剛建立的provider項目。

如今開始改造Eureka-Client 項目:

  1. 引入 spring-cloud-openfeign 組件

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  2. 在啓動類上添加註解@EnableFeignClients,啓用Feign的客戶端功能

  3. 定義Feign接口,@FeignClient 註解指定了服務名稱

    @FeignClient(value = "eureka-provider")
    public interface ProviderFeign{
        /**
         * 調用 eureka-provider 的 hello 接口
         * @return
         */
        @RequestMapping("/hello")
        String hello();
    }
  4. 定義sayHello接口,經過feign調用provider的接口

    @RestController
    public class SayHelloController{
    	  @Autowired
    	  private ProviderFeign providerFeign;
    	  @GetMapping("sayHello")
    	  public String sayHello(){
    	      return providerFeign.hello();
    	  }
    }
  5. 重啓Eureka-Client 項目,訪問http://localhost:8081/sayHello。頁面顯示hello, my name is eureka provider! 表示咱們使用OpenFeign發起服務間調用成功。

至此一個簡單使用OpenFeign發起服務間的調用示例就完成了,下面的教程是進階版,瞭解一下仍是很是有必要的。

使用OpenFeign的其餘小技巧

Get 請求以對象做爲參數提交

當服務提供者定義了一個Get請求的接口,參數是一個對象,好比這樣的:

@RequestMapping(method = RequestMethod.GET, path = "query")
public String query(UserDTO user){

當服務調用方Feign使用@QueryMap來進行接口調用

@RequestMapping("/query")
String query(@QueryMap UserDTO userDTO);

這時候會發生服務提供方接收到的請求變爲Post的現象,服務提供者接收到的請求報錯信:

Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]

這種問題怎麼解決呢?

  1. 把@QueryMap 註解換成@SpringQueryMap註解就能夠,這是最簡單快速的解決辦法。
     

把Feign 默認的 Client 替換成OKHttp

Feign 中默認使用 JDK 原生的 URLConnection 發送 HTTP 請求,咱們能夠把它換成httpclient或者OkHttp,添加以下配置便可:

# 啓用okhttp
feign.okhttp.enabled=true
feign.httpclient.enabled=false

若是你不是用的spring-cloud-dependencies,或者裏面沒有okhttp的包,本身引入便可:

<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-okhttp</artifactId>
</dependency>

配置Feign日誌輸出

FeignClient 有一個屬性configuration,咱們能夠經過這個屬性來自定義每一個FeignClient的日誌輸出

  1. 新建一個配置類ProviderFeignConfiguration:

    import feign.Logger;
    ...
    @Configuration
    public class ProviderFeignConfiguration{
        @Bean
        public Logger.Level loggerLevel(){
            return Logger.Level.BASIC;
        }
    }

    Feign日誌記錄的級別一共有4種:NONE、BASIC、HEADERS、FULL;

  2. 爲@FeignClient指定配置

    @FeignClient(value = "eureka-provider",configuration = ProviderFeignConfiguration.class)
  3. 爲FeignClient包所在位置單獨配置日誌隔離級別

    logging.level.cn.jinglingwang.eurelaclient.demo.feign=DEBUG

    這一步你也能夠不這樣作,能夠經過自定義繼承 feign.Logger 重寫log方法便可輸出日誌。

  4. 重啓項目,訪問接口http://localhost:8081/sayHello,查看日誌輸出變化:

    DEBUG 20000 --- [nio-8081-exec-4] c.j.e.demo.feign.ProviderFeign           : [ProviderFeign#hello] ---> GET http://eureka-provider/hello HTTP/1.1
    DEBUG 20000 --- [nio-8081-exec-4] c.j.e.demo.feign.ProviderFeign           : [ProviderFeign#hello] <--- HTTP/1.1 200 (4ms)

配置Auth認證

Feign提供了一個默認的攔截器BasicAuthRequestInterceptor,他主要的功能是爲發起的Http請求添加一個請求頭:template.header("Authorization", headerValue);
使用方法:

  1. 在剛剛上面的ProviderFeignConfiguration類裏面添加如下代碼便可:

    @Bean
    public BasicAuthRequestInterceptor basicAuth(){
        return new BasicAuthRequestInterceptor("username","jinglingwang.cn");
    }
  2. 改造下provider的接口代碼,輸出Header看是否能輸出這個字段

    @ResponseBody
    @RequestMapping(method = RequestMethod.GET, path = "hello")
    public String hello(HttpServletRequest request) throws UnsupportedEncodingException{
        String header = request.getHeader("Authorization");
        if(header != null && header.length() > 6){
            String authorization = new String(Base64.decode(header.substring(6).getBytes("UTF-8")),"UTF-8");
            System.out.println(authorization);
        }
        return "hello, my name is eureka provider!";
    }
  3. 重啓兩個項目,訪問http://localhost:8081/sayHello,查看provider控制檯成功輸出如下內容:

    username:jinglingwang.cn

配置超時時間

  1. 在剛剛上面的ProviderFeignConfiguration類裏面添加如下代碼:

    @Bean
    public Request.Options options(){
        return new Request.Options(5,TimeUnit.SECONDS,5,TimeUnit.SECONDS,true);
    }

    上面參數分別的意思是:鏈接超時5秒,讀取超時5秒,true:遵循3xx重定向

經過配置文件配置Feign

上面的配置基本上都是經過Java代碼的方式來進行的,其實也能夠經過配置文件來配置Feign,經過feign.client.config.{feignName}.xxx 來進行配置,好比:

# 單獨配置Feing:eureka-provider的鏈接超時時間 1ms
feign.client.config.eureka-provider.read-timeout=1

重啓以後刷新接口http://localhost:8081/sayHello,出現超時的日誌:

具體能夠配置的配置項見下圖:

注意:配置文件配置的優先級是大於上面Java代碼配置的優先級的,從上面的測試結果也能夠看出,由於咱們同時使用了兩種配置方式(重啓時Java 的配置並無註釋),從下圖所示的源碼也能夠看出:

總結

  1. Feign 支持原生的API和聲明式註解兩種方式發起請求
  2. 啓用Feign客戶端須要使用@EnableFeignClients註解來開啓
  3. FeignClient 支持配置文件和Java配置來控制,配置文件的優先級更高
相關文章
相關標籤/搜索