Spring Cloud Alibaba學習筆記(4) - Feign配置與使用


什么是Feign

Feign是一個聲明式Web Service客戶端。
使用Feign能讓編寫Web Service客戶端更加簡單, 它的使用方法是定義一個接口,然后在上面添加注解,同時也支持JAX-RS標准的注解。Feign也支持可拔插式的編碼器和解碼器。
Spring Cloud對Feign進行了封裝,使其支持了Spring MVC標准注解和HttpMessageConverters。Feign可以與Eureka和Ribbon組合使用以支持負載均衡。

Feign的組成

接口 作用 默認值
Feign.Builder Feign的入口 Feign.Builder
Client Feign底層用什么去請求 和Ribbon配合時:LoadBalancerFeignClient(代理模式,可以為Feign引入連接池) 不和Ribbon配合時:Fgien.Client.Default(URLConnection,沒有連接池,沒有資源管理,性能較差)
Contract 契約,注解支持 SpringMVCContract
Encoder 解碼器,用於將獨享轉換成HTTP請求消息體 SpringEncoder
Decoder 編碼器,將相應消息體轉成對象 ResponseEntityDecoder
Logger 日志管理器 Slf4jLogger
RequestInterceptor 用於為每個請求添加通用邏輯

整合Feign

在pom.xml文件中添加依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在啟動類上添加@EnableFeignClients注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import tk.mybatis.spring.annotation.MapperScan;

// 掃貓Mybatis哪些包里面的接口
@MapperScan("com.example")
@SpringBootApplication
@EnableFeignClients
public class Study01Application {

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

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

使用Feign實現遠程http調用

創建feignClient包,用來存放Feign接口,因為我整合過程中調用的是comment接口,所以創建CommentFeignClient接口類。
添加注解@FeignClient,表明這個接口是一個FeignClient,指定name,name是要請求的微服務的名稱。
這時,就可以在接口中添加方法了,方法注解參照spring mvc的注解,代碼如下:

@FeignClient(name = "study02")
public interface CommentFeignClient {
    @GetMapping("/find")
    DemoComment find();
}

在前文中,微服務A如果要調用微服務B的請求,參考代碼如下:

private final DiscoveryClient discoveryClient;
private final RestTemplate restTemplate;

public DemoComment findById() {
    // 獲取請求示例
    List<ServiceInstance> instances = discoveryClient.getInstances("study02");
    List<String> collect = instances.stream()
            .map(instance -> instance.getUri().toString() + "/find")
            .collect(Collectors.toList());
    // 隨機算法
    int i = ThreadLocalRandom.current().nextInt(collect.size());
    String targetURL =  collect.get(i);
    DemoComment forObject = restTemplate.getForObject("http://study02/find", DemoComment.class, 1);
    return forObject;
}

其中, DemoComment forObject = restTemplate.getForObject("http://study02/find", DemoComment.class, 1);是具體的請求代碼,我們借用RestTemplate接口實現了服務間的請求調用。
那么,利用Feign又如何做到呢?

  • 首先,我們將restTemplate注入更換為剛剛添加的CommentFeignClient注入
  • 然后直接使用commentFeignClient.***()調用請求就可以了。

代碼如下:

private final DiscoveryClient discoveryClient;
private final CommentFeignClient commentFeignClient;

public DemoComment findById() {
    DemoComment forObject = commentFeignClient.find();
    return forObject;
}

Feign的配置

和Ribbon一樣,Feign也支持兩種配置方式:java代碼方式及配置屬性模式

Feign支持的配置項

代碼支持的配置項

配置項 作用
Logger.Level 指定日志級別
Retryer 指定重試策略
ErrorDecoder 指定錯誤解碼器
Request.Options 超時時間
Collection 攔截器
SetterFactory 用於設置Hystrix的配置屬性,Fgien整合Hystrix才會用

屬性支持的配置項

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000  # 相當於Request.Optionsn 連接超時時間
        readTimeout: 5000     # 相當於Request.Options 讀取超時時間
        loggerLevel: full     # 配置Feign的日志級別,相當於代碼配置方式中的Logger
        errorDecoder: com.example.SimpleErrorDecoder  # Feign的錯誤解碼器,相當於代碼配置方式中的ErrorDecoder
        retryer: com.example.SimpleRetryer  # 配置重試,相當於代碼配置方式中的Retryer
        requestInterceptors: # 配置攔截器,相當於代碼配置方式中的RequestInterceptor
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        # 是否對404錯誤解碼
        decode404: false
        encode: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

下面以feign的日志級別自定義為例展示feign的配置

細粒度配置

Java代碼

首先,添加Feign配置類,可以添加在主類下,但是不用添加@Configuration。如果添加了@Configuration而且又放在了主類之下,那么就會所有Feign客戶端實例共享,同Ribbon配置類一樣父子上下文加載沖突;如果一定添加@Configuration,就放在主類加載之外的包。建議還是不用加@Configuration。

import feign.Logger;
import org.springframework.context.annotation.Bean;

public class DemoFeignConfiguration {
    @Bean
    public Logger.Level level() {
        // 讓Feign打印所有請求的細節
        return Logger.Level.FULL;
    }
}

然后,給@FeignClient添加配置類

@FeignClient(name = "study02", configuration = DemoFeignConfiguration.class)
public interface CommentFeignClient {
    @GetMapping("/find")
    DemoComment find();
}

因為示例的是日志級別自定義,而feign的日志級別是建立在feign的接口的日志級別是debug的基礎上的,所以需要添加配置屬性

logging:
  level:
    #需要將FeignClient接口全路徑寫上
    com.example.study01.feignClient.CommentFeignClient: debug

配置屬性

feign:
  client:
    config:
      #想要調用的微服務名稱,不是本身
      study02:
        loggerLevel: FULL

全局配置

Java代碼

在啟動類上為@EnableFeignClients注解添加defaultConfiguration配置

@EnableFeignClients(defaultConfiguration = DemoFeignConfiguration.class)

配置屬性

feign:
  client:
    config:
      #將調用的微服務名稱改成default就配置成全局的了
      default:
        loggerLevel: FULL

優先級:細粒度屬性配置 > 細粒度代碼配置 > 全局屬性配置 > 全局代碼配置

打印出來的信息如下:

2019-10-22 14:55:41.286 DEBUG 31468 --- [nio-8881-exec-1] c.e.s.feignClient.CommentFeignClient     : [CommentFeignClient#find] <--- HTTP/1.1 200 (425ms)
2019-10-22 14:55:41.286 DEBUG 31468 --- [nio-8881-exec-1] c.e.s.feignClient.CommentFeignClient     : [CommentFeignClient#find] content-type: application/json;charset=UTF-8
2019-10-22 14:55:41.286 DEBUG 31468 --- [nio-8881-exec-1] c.e.s.feignClient.CommentFeignClient     : [CommentFeignClient#find] date: Tue, 22 Oct 2019 06:55:41 GMT
2019-10-22 14:55:41.286 DEBUG 31468 --- [nio-8881-exec-1] c.e.s.feignClient.CommentFeignClient     : [CommentFeignClient#find] transfer-encoding: chunked
2019-10-22 14:55:41.286 DEBUG 31468 --- [nio-8881-exec-1] c.e.s.feignClient.CommentFeignClient     : [CommentFeignClient#find] 
2019-10-22 14:55:41.286 DEBUG 31468 --- [nio-8881-exec-1] c.e.s.feignClient.CommentFeignClient     : [CommentFeignClient#find] {"id":1,"typeId":0,"valueId":1152101,"content":"MTE=","addTime":1504933578,"status":0,"userId":15}
2019-10-22 14:55:41.286 DEBUG 31468 --- [nio-8881-exec-1] c.e.s.feignClient.CommentFeignClient     : [CommentFeignClient#find] <--- END HTTP (98-byte body)
2019-10-22 14:55:42.142  INFO 31468 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: study02.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

Feign多參數請求構造

GET請求

使用@SpringQueryMap

@FeignClient(name = "study02")
public interface CommentFeignClient {
    @GetMapping("/find")
    public DemoComment query(@SpringQueryMap DemoComment comment);
}

使用 @RequestParam

@FeignClient(name = "study02")
public interface CommentFeignClient {
    @RequestMapping(value = "/find",method = RequestMethod.GET)
    public DemoComment query(@RequestParam("id") Long id, @RequestParam("name") String name);
}

使用Map構建(不推薦)

@FeignClient(name = "study02")
public interface CommentFeignClient {
    @RequestMapping(value = "/find",method = RequestMethod.GET)
    public DemoComment query(@RequestParam Map<String,Object> map);
}

POST請求

服務提供者方法

 @PostMapping("/save")
    public DemoComment save(@RequestBody DemoComment comment){
        return null;
    }

服務調用者

@FeignClient(name = "study02")
public interface CommentFeignClient {
    @RequestMapping(value = "/save",method = RequestMethod.POST)
    public DemoComment query(@RequestBody DemoComment comment);
}

Feign性能優化

配置連接池

apache httpClient

添加依賴

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

添加配置

feign:
  httpclient:
    # 讓feign使用apache httpclient作請求
    enabled: true
    # feign的最大連接數
    max-connections: 200
    # feign單個路徑的最大連接數
    max-connections-per-route: 50

okHttp

加依賴

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

添加配置

 httpclient:
    # feign 最大連接數
    max-connections: 200
    # feign 單個路徑請求的最大連接數
    max-connections-per-route: 50
  okhttp:
    enabled: true

合理使用Feign日志

生產環境使用 Logger.Level.BASIC


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM