问题起因:
当jenkins发布product项目时,访问后台会报出500错误码
排查过程:
项目都有负载均衡,正常不会有此类问题,初始判断可能是缓存问题,导致访问没有到其他节点上,跟开发沟通,排除了这个可能
然后判断可能是因为nacos上服务列表没有刷新,关闭的项目节点还在服务列表中,导致一直有访问到关闭的项目节点
多次尝试发布product项目,然后访问网站,检测出持续报出500错误码的时间大概30秒
修改nacos服务列表的刷新时间,将参数getUpdatedListOfServers降低到15秒,再次测试后发现没变化,而且每次报错的时长都在30秒左右
正常来说即便是服务列表没有及时刷新,也不可能每次都是30秒,这相当于每次发布刚好卡在列表刚刷新完的那个时间点,这在概率上来讲是不可能的
所以判断也不是这个问题,排查的方向错了
但是也因此可以发现,即便是刷新了服务列表,已经停止的项目依然在服务列表中,查看nacos管理界面的服务列表
果然,即便发布过程中,已经将一个节点停止了,但是在nacos的服务列表中还显示在线,而且还是绿的,大概过15秒才会变成红色,然后再过15秒被释放
问题原因:
根据排查的结果,可以判断出,当节点的项目停止时,没有给nacos发送’我要停止’的消息,导致nacos以为节点的项目还在线,网关服务也就一直给停止的节点发送数据
解决方法:
nacos作为注册中心,支持两种服务下线,一种是客户端主动调用api向服务端发送服务下线,然后实现服务下线,第二种就是服务故障,然后服务端很长时间没有收到某个实例的心跳信息,服务端就会将这个服务健康状态设置成false,也就是标志不健康状态(这个时间默认是15s,也就是15s服务端没有收到某个服务的心跳信息),如果更长时间没有收到心跳信息,直接就会将这个服务摘除(默认是30s)。
根据上述原理还需要查看到底是客户端没有调用api向服务端发送服务下线,还是服务故障
先看后者,后者较容易排查,而且前者很可能是开发的问题,所以此处使用排除法
后者所说的服务故障,一般来讲是强制关闭或者网络中断,此处排除网络中断,强制关闭需要看docker关闭容器的过程
节点的服务是通过docker进行部署,发布过程中停止容器肯定用的是docker stop的方法,当你通过命令 docker stop mycontainer
来停止容器时,docker CLI 会将 TERM
信号发送给 mycontainer 的 PID
为 1 的进程,这就需要看 PID
为 1 的进程到底是什么(此处引入了docker 优雅关闭容器的问题,可以查看我的另一个文章https://www.wmmzz.com/docker-youyaguanbirongqi/),根据排查和调整,用strace命令跟踪信号值
也就是说docker stop 并没有让服务强制关闭,而是服务本身没有调用api向服务端发送服务下线通知
最终解决的方法应该是让开发把这个下线通知的功能加上,但是在解决这个问题的过程中也找到了一个其他方法,这个方法也可以让服务下线,可以看我这个文章https://www.wmmzz.com/springbootxiangmulianjienacosshiyouyaguanbi/
更新:
开发将下线通知nacos的代码加上了,关闭后的日志输出如下:
根据实际测试,关闭docker容器服务后,在nacos里服务列表中的记录就直接没了,这种方法比上面用curl命令的方法更好
但是改用这种方式后,再测试发布还是会有code 500的问题
期间又将ribbon下的很多参数进行了调整,但是都不理想,只有将ServerListRefreshInterval参数调低,才能有所改善,可是还会有code 500,只是时间变短了
ribbon:
eureka:
enabled: true
http:
client:
enabled: true
okhttp:
enabled: false
## 从注册中心刷新servelist的时间 默认30秒,单位ms
ServerListRefreshInterval: 2000
## 请求连接的超时时间 默认1秒,单位ms
ConnectTimeout: 1000
## 请求处理的超时时间 默认1秒,单位ms
ReadTimeout: 30000
## 对所有操作请求都进行重试,不配置这个MaxAutoRetries不起作用 默认false
OkToRetryOnAllOperations: true
## 对所有错误进行重试
OkToRetryOnAllErrors: false
## 跟服务端建立连接时出现错误
OkRetryOnConnectionErrors: true
## 切换实例的重试次数 默认1
MaxAutoRetriesNextServer: 2
## 对当前实例的重试次数 默认0
MaxAutoRetries: 0
retryableStatusCodes: 500,404,502
restclient:
enabled: true
最终解决办法:
其实还是配置的问题,网关服务本身也有重试机制,在nacos的网关服务配置文件中加入以下代码,最终发布不再有code 500
default-filters:
- DedupeResponseHeader=Vary Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_FIRST
- name: Retry
args:
retries: 3
statuses:
- BAD_GATEWAY
- SERVICE_UNAVAILABLE
- GATEWAY_TIMEOUT
## GET,POST,PUT
methods:
- GET
- PATCH
- DELETE
- OPTIONS
- HEAD
exceptions:
- org.springframework.cloud.gateway.support.NotFoundException
- java.io.IOException
- org.springframework.cloud.gateway.support.TimeoutException
配置结构在spring.cloud.gateway.下面,以下是结构比较全面的配置
spring:
cloud:
sentinel:
eager: true
transport:
dashboard: ${meta.sentinel.base-url}
heartbeat-interval-ms: 2000
filter:
enabled: true
datasource:
flow:
nacos:
namespace: ${meta.gateway.nacos.namespace}
server-addr: ${meta.gateway.nacos.address}
dataId: ${spring.application.name}-sentinel-flow-rules
groupId: SENTINEL_GROUP
ruleType: flow
degrade:
nacos:
namespace: ${meta.gateway.nacos.namespace}
server-addr: ${meta.gateway.nacos.address}
dataId: ${spring.application.name}-sentinel-degrade-rules
groupId: SENTINEL_GROUP
ruleType: degrade
system:
nacos:
namespace: ${meta.gateway.nacos.namespace}
server-addr: ${meta.gateway.nacos.address}
dataId: ${spring.application.name}-system-rules
groupId: SENTINEL_GROUP
rule-type: system
authority:
nacos:
namespace: ${meta.gateway.nacos.namespace}
server-addr: ${meta.gateway.nacos.address}
dataId: ${spring.application.name}-authority-rules
groupId: SENTINEL_GROUP
rule-type: authority
param-flow:
nacos:
namespace: ${meta.gateway.nacos.namespace}
server-addr: ${meta.gateway.nacos.address}
dataId: ${spring.application.name}-param-flow-rules
groupId: SENTINEL_GROUP
rule-type: param-flow
loadbalancer:
retry:
enabled: true
gateway:
discovery:
locator:
enabled: false
lowerCaseServiceId: true
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "*"
allowedHeaders: "*"
allowedMethods: "*"
default-filters:
- DedupeResponseHeader=Vary Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_FIRST
- name: Retry
args:
retries: 3
statuses:
- BAD_GATEWAY
- SERVICE_UNAVAILABLE
- GATEWAY_TIMEOUT
## GET,POST,PUT
methods:
- GET
- PATCH
- DELETE
- OPTIONS
- HEAD
exceptions:
- org.springframework.cloud.gateway.support.NotFoundException
- java.io.IOException
- org.springframework.cloud.gateway.support.TimeoutException
参考文献:
https://www.jianshu.com/p/5c4903cd328b
https://www.jianshu.com/p/62bee46faa02
https://blog.csdn.net/yuanshangshenghuo/article/details/111290929
https://blog.csdn.net/yuanshangshenghuo/article/details/111299177