微服务改造之Openfeign的强化插件

在接触 Spring Cloud 这套框架之前,笔者使用的一直是Dubbo。在转型到Spring Cloud 后,发现了一个很郁闷的问题。Spring Cloud 中的 Openfeign,相比于 Dubbo 对单个方法和单个接口(Interface)设置RPC超时时间,支持的不是特别理想。我查阅了一些资料和博客发现,现有的 Feign 只支持RPC超时设置的方式主要有以下几种

第一种配置默认的连接超时和读取超时,该配置全局生效。

feign.client.config.default.connectTimeout=2000
feign.client.config.default.readTimeout=5000

第二种是设置服务级别的连接超时和读取超时,该配置对这个服务的全部接口生效。

# 单个服务 连接超时时间
feign.client.config.{这里填写服务名称}.connectTimeout = 5000
# 单个服务 读超时时间
feign.client.config.{这里填写服务名称}.readTimeout = 10000

以上两种的粒度都非常粗,所以使用起来并不是很理想。下面要介绍的方案避免了这个粗粒度的问题。

第三种是借助于Hystrix实现方法级别的超时时间设置:

#首先是开启 Hystrix
feign.hystrix.enabled = true

# 全局设置超时
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 30000

#服务级别设置超时
hystrix.command.{这里填写服务名称}.execution.isolation.thread.timeoutInMilliseconds: 30000

要对以下接口进行设置:

public interface MemberService {
    @RequestMapping("/getUsers")
    List<User> getUsers(String name);
}
# 方法级别设置超时
hystrix.command.MemberService#getUser(String).execution.isolation.thread.timeoutInMilliseconds = 3000

分析一下一下几种方法的优劣:

首先以上几种方法都是可以使用的没有问题,只是用起来不是特别爽快,相比于Dubbo总感觉缺点东西。第一种和第二种粒度太大,如果只是有一个方法需要很长的时间,如果只是为了这个方法,而将这个服务下所有的方法都设置成同样长的超时时间,显然是不太合适的。而第三种方法又要开启Hystrix,说句老实话,Hystrix现在新的项目使用的越来越少,就像笔者现在使用的是Sentinel。而且Hystrix的超时设置比较奇特用并不是我们所理解的Rpc 连接超时和读取超时,而是执行超时时间。如果有兴趣的同学可以自己去了解一下,因为篇幅有限这里就不展开讲了。

各位看官看到这里是不是要开始吐槽了,讲了大半天了,讲的都是大家知道的,一点干货都没有,说好的强化插件呢???

 

各位大大不要着急,下面就要开始介绍Feign的强化插件,集成了连接超时,读取超时和重试。分为服务提供方的注解形式和服务消费方的配置形式。该插件的的作用在于补全openfeign客户端在使用的默认代理和 Sentinel 代理 的过程中无法自定义单个接口或者单个方法级别的超时时间与重试的插件。

本插件的优先级为:

服务消费者method配置 > 服务提供者method > 服务消费者interface级配置 > 服务提供者interface级配置 > 服务消费者全局配置
注意:是否允许重试以服务提供者注解标识为最高优先级,只有服务提供方注解显示的表明可以重试,才能执行重试。如果注解没有,默认不可重试。

服务提供方 maven 引用:

            <dependency>
                <groupId>org.example</groupId>
                <artifactId>openfeign-strengthen-plugin-annotation</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>

java 代码书写方式(服务提供方使用注解形式):

 /**
             * 注解放在 interface 上,则该 interface 下所有的方法 共享该配置
             */
            @RpcInfo(connectTimeout = 2,connectTimeoutUnit = TimeUnit.SECONDS, readTimeout = 1, readTimeoutUnit = TimeUnit.SECONDS, followRedirects = false)
            public interface MemberService {
            
                /**
                 * 注解放在 method 上
                 * 则该 method  RPC调用 时使用该配置。
                 * 方法上的配置优先级大于 interface 级别,
                 * 同时存在时,以 method 上的为准
                 * connectTimeout 连接超时时间
                 * connectTimeoutUnit 连接超时时间的单位 默认为毫秒
                 * readTimeout 读取超时时间
                 * readTimeoutUnit 读取超时时间单位 默认为毫秒
                 * followRedirects 是否重定向 默认为否
                 * maxAutoRetriesNextServer 切换实例的重试次数 默认为 0 次 禁止重试
                 * maxAutoRetries 对当前实例的重试次数 默认为 0 次 禁止重试
                 * isAllowedRetry 是否允许重试 默认为 false 不允许
                 */
                @RequestMapping("/getUsers")
                @RpcInfo(connectTimeout = 2,connectTimeoutUnit = TimeUnit.SECONDS, 
                        readTimeout = 1, readTimeoutUnit = TimeUnit.SECONDS, followRedirects = false, 
                        maxAutoRetriesNextServer = 1, maxAutoRetries = 1, isAllowedRetry = true)
                List<User> getUsers(String name);
            
                @PostMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
                @RpcInfo(connectTimeout = 5,connectTimeoutUnit = TimeUnit.SECONDS, readTimeout = 2, readTimeoutUnit = TimeUnit.SECONDS)
                String handleFileUpload(@RequestPart(value = "file") MultipartFile file);
            }

服务消费者引入 maven 依赖:

        <dependency>
             <groupId>org.example</groupId>
             <artifactId>openfeign-strengthen-plugin-start</artifactId>
             <version>1.0-SNAPSHOT</version>
        </dependency>

服务消费者方使用 application.yml 配置的形式使用,并且服务消费者超时时间配置优先级高于服务提供方配置

  feign:
    client:
      rpcConfig:
        #单个方法维度的配置
        #接口名称 + # + 方法名称 + (参数类型, 参数类型...)
        #包名 + 接口名称 + # + 方法名称 + (参数类型, 参数类型...) com.huihuang.service.MemberService#getUsers(String)
        MemberService#getUsers(String):
          connectTimeout: 1 #连接超时
          connectTimeoutUnit: SECONDS #超时时间单位
          readTimeout: 1 #读取超时
          readTimeoutUnit: SECONDS
          followRedirects: false #是否允许重定向
          maxAutoRetries: 2 # 对当前实例的重试次数
          maxAutoRetriesNextServer: 1 # 切换实例的重试次数
        #按接口维度配置 优先级低于 单个方法维度的配置
        #接口名称
        #包名 + 接口名称 com.huihuang.service.MemberService
        MemberService:
          connectTimeout: 1 #连接超时
          connectTimeoutUnit: SECONDS #超时时间单位
          readTimeout: 1 #读取超时
          readTimeoutUnit: SECONDS
          followRedirects: false #是否允许重定向
          maxAutoRetries: 2 # 对当前实例的重试次数
          maxAutoRetriesNextServer: 1 # 切换实例的重试次数
        #全局维度配置 优先级低于 单个方法维度 和 接口维度 的配置
        default:
          connectTimeout: 1 #连接超时
          connectTimeoutUnit: SECONDS #超时时间单位
          readTimeout: 1 #读取超时
          readTimeoutUnit: SECONDS
          followRedirects: false #是否允许重定向
          maxAutoRetries: 2 # 对当前实例的重试次数
          maxAutoRetriesNextServer: 1 # 切换实例的重试次数

GitHub地址://github.com/RaidenXin/openfeign-strengthen-plugin

这是我自己遇到的问题,希望能给各位看官大大一点点帮助。如果有什么意见和建议欢迎和我联系,可以直接评论或者在我的微信公众号后台留言发给我,笔者现在在公司的职位是基础架构架构师,如果有其他的Java方面或者基础架构方面的问题也可以一起交流一下。非常感谢大家的观看。

下面是我的公众号: