背景
最近在做微服务拆分,最终选用的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
总结
目前的解决方式大致是 优雅停机+减少客户端缓存时间。后续还是需要有更好的优化方案
原文始发于微信公众号(小奏技术):Spring Cloud openfeign 处理服务平滑上下线
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30059.html