Spring Cloud之openfeign 处理服务平滑上下线

这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党

背景

最近在做微服务拆分,最终选用的RPC框架为 openfeign。本以为使用是非常简单的,毕竟网上和官网的demo使用起来很简单,无非就是加几个注解。但是在实际落地的时候还是遇到了很多问题,这里就先说服务上下线问题吧

问题

在服务拆分上线后,服务上线重启经常会发现其他服务报这个两个错

feign.RetryableException: connect timed out executing GET 
复制代码

服务连接超时

Unexpected end of file from server executing GET xxx
复制代码

xxx 接口不可用

问题定位

其实这个问题相对来说是非常好定位的,因为每次报这个错都是由于服务重启才会出现的。 比如A服务重启, B服务就会报这个错

首先 xxx接口不可用这个主要原因是在服务A调用服务B的时候,由于服务B接口还没响应,突然服务B被 kill -9 强杀了,就会报这个错

feign.RetryableException: connect timed out executing GET 这个错误主要是由于Feign客户端的负载均衡导致的,客户端会从注册中心拉取服务列表的地址,本地会缓存一份。导致的问题是服务下线后注册中心是能感知的,但是由于客户端缓存问题,导致了客户端这边调用的还是原先已经下线的服务,从而报错

问题解决

Unexpected end of file from server executing GET xxx

这个问题比较好解决,我们要解决的就是服务不要直接被强制杀死,要求服务处理完正在运行的请求,再停机。在Spring Boot 2.3之后就添加了优雅停机。

使用方式也非常简单,添加如下配置

server:
  shutdown: graceful #开启优雅停机
spring:
  lifecycle:
    timeout-per-shutdown-phase: 20s #设置缓冲时间 默认30s
复制代码

如果Spring Boot 版本小于2.3,官方没有提供相应的功能,但是我们可以自己实现一个。代码如下

public class SpringStopListener implements ApplicationListener<ContextClosedEvent>, TomcatConnectorCustomizer {

    private volatile Connector connector;
    
	@Override
    public void customize(Connector connector) {
        this.connector = connector;
    }
    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        this.connector.pause();
        Executor executor = this.connector.getProtocolHandler().getExecutor();
        if (executor instanceof ThreadPoolExecutor) {
            try {
                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
                // 将状态设置为shutdown,不再接收新的请求,正在跑的任务会执行完
                threadPoolExecutor.shutdown();
                if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
                    log.warn("Tomcat thread pool did not shut down gracefully within " + waitTime + " seconds. Proceeding with forceful shutdown");
                }
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }


}
复制代码

feign.RetryableException: connect timed out executing GET

这个问题相对来说不是特别好处理,主要原因是由于客户端缓存了注册中心实例元数据导致的,如果禁用客户端缓存又会导致性能不佳,每次都需要去注册中心拉取服务数据,如果不禁用服务上线的时候就会出现这个报错。 首先Spring Cloud Feign的负载均衡在不同版本又有负载均衡处理器,老版本使用的Ribbon,新版本的Spring Cloud已经将Ribbon替换成Spring Cloud Load Balancer,毕竟Netfix 已经不在维护了Ribbon

Ribbon

如果你使用的负载均衡器是Ribbon,你可以使用如下配置

ribbon:
  ServerListRefreshInterval: 2000 # 客户端缓存元数据时间
  ReadTimeout: 5000 # 读取数据超时时间
  ConnectTimeout: 2000 # 连接超时时间
复制代码

这个ServerListRefreshInterval值是一点要改的,默认的客户端缓存注册中心服务数据时间为30s

在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

这个时间是真的坑。我们这里折中改为2s

Spring Cloud Load Balancer

Spring Cloud Load Balancer 目前来说网上的资料较少,想要配置还需要自己去官网找资料 目前禁用的配置是如下

spring:
  cloud:
    loadbalancer:
      cache:
        enabled: false
复制代码

在这里插入图片描述

总结

目前的解决方式大致是 优雅停机+减少客户端缓存时间。后续还是需要有更好的优化方案

Guess you like

Origin juejin.im/post/7068886236303687687