项目背景
随着微服务的流行,越来越多的团队默认以垂直的方式按业务将各个领域模型独立出来,作为一个服务对外提供业务支持;目前团队采用的注册中心是nacos,大体骨架如下:
事情起因
前端同学反馈说测试环境登录很慢,登录嘛,大体逻辑就是表单采集提交的用户名和密码,后端验证是否一致。没有说不能登录,就是单纯的慢,刚开始以为是网络问题,一直没当回事,一次反馈可以不管,慢慢的发现每个前端同学都在喊,肯定哪里有问题,开始排查。
2.1 在验证账密的服务中增加日志,从收到请求到验证通过,时间基本就固定在ms范围内,排除这附近不会有问题。
2.2 抓包,查看耗时在哪个阶段,这里耽误了一些时间,装错软件了,大家要试的话千万不要点错了
参考:https://blog.csdn.net/qq_42846555/article/details/127745404
最终定位是在tcp链接花了很长时间,为什么呢?
2.3 转战服务器,使用命令:netstat -n | awk '/^tcp/ {++S[$NF]} END {for (a in S) print a, S[a]}' 可以看到存在大量处于TIME_WAIT的TCP状态:
查询相关资料,当服务器上该状态过多时不正常的,如何控制呢?
解决方案
这里面说了原因跟解决方案:https://zhuanlan.zhihu.com/p/419752048,想着是测试环境(生产环境一定不要自己弄 x 3),应该出不了大问题所以我照着配置了下:
允许将TIME_WAIT状态的socket重新用于新的TCP连接
#默认为0,表示关闭 vim /etc/sysctl.confnet.ipv4.tcp_tw_reuse = 1
快速回收TIME_WAIT状态的socket
#默认为0,表示关闭 vim /etc/sysctl.confnet.ipv4.tcp_tw_recycle = 1
果然问题得以解决。但还有一个问题,为什么会产生这么多time_wait?上面的方案属于治标不治本!
意料之外
4.1 我的这个改动导致sit环境jenkins发布时而成功时而失败,失败的时候同学都去找运维定位,运维查了几天没找到什么原因,这个时候大王出现了:
我说不是也不行,因为前些天问题解决后群里有记录,认错后运维同学将配置改了回去,但按起葫芦起了瓢,他的解决了我这边问题又复现了,还是要解决根本问题——排查time_wait的来源,上面我用的是统计命令,其实可以稍微改变一下:netstat -an | grep tcp | grep TIME_WAIT | more
可以发现其实多数都是8848端口,而这个端口是注册中心nacos服务默认的端口,所以根因就是nacos定时心跳检测注册到上面的服务是否健康,当微服务越多,nacos要检测的服务就越多,产生的time_wait就越多!
4.2 为什么登录校验会很慢呢?因为我们测试环境资源紧张部署不合理导致,如下图:
正常逻辑,前端请求经过gateway后通过注册中心查询服务完成账密校验,但因gateway和nacos部署在同一台机器,导致在查找认证服务以前用户的请求在实例A上被长时间堵住。
4.3 上面试过配置linux服务器的内核参数,但运维同学不建议修改,如何解决这一问题呢?阿里的产品阿里云(https://developer.aliyun.com/ask/523406)社区自然有答案:
1改了被要求回滚,2、4一般不会考虑,只能针对3下狠手,墙裂建议阁下看一下:https://blog.51cto.com/u_15464526/4850302,其实是nacos低版本的一个已知bug,已经有前辈提issue了。我们要做的就是到maven仓库找到对应的坐标升级就行,至此,问题被彻底解决。
连锁反应
一个简单的升级谁会在意影响面有多大,版本发布后99%的业务正常运行。某天一个业务触发了异常:
public interface ApiManageClient {
// 获取坐标对应的为省市
@GetMapping("/restapi/route/geocode/regeo")
BaseResponseEntity<JsonNode> coordinateToCityCodeByRegeo(@RequestParam("location") String location);
}
大致就是调用这个服务,返回的结果无法反序列化触发了异常,把锅甩给开发这个服务的同学——对,没有错,简简单单就是我。
按照高德API的说明,如此简单的一个接口怎么会有问题,或者说怎么会突然有问题,这个服务其它部门的同学也在调用,只有这边反应有bug,难道不是调用方自己的锅,还有脸找我?
首先证明我服务正常,找了几个其他端的同学,发现也正常。但这边同学一口咬定——就是我的锅。
找测试同学看看,是否能复现,讲道理,每次发版这些业务都有回归,如果有问题测试环境也一定有问题,但巧的就是测试同学也说这版本在回归时并无相关问题才正常发版。
突然意识到上一篇《OpenFeign get请求传递参数异常》我踩过坑,feign有时候会改变参数,先看看应用收到请求打印出来的参数是啥:
应该以逗号(,)分隔的经纬度参数,中间被encoder成%2C,更早前这个服务没出问题,代码就是简单的字符串拼接,查看git history显示没人改过,代码还是:
String location = xxx.getLng() + "," + xxx.getLat();
apiManageClient.coordinateToCityCodeByRegeo(location);
怪不得我让其它同学试没问题,因为他们是前端和python,请求未经过feign。现在问题变成同样底层是feign,为什么以前没问题突然出问题了,试着让测试回退版本,回退v2.51到v.2.50收到的参数变为正常,得到失败跟A服务发版有关,如下图:
如何取舍
本次发布A服务,除了悄悄的升级maven坐标解决time_wait过多,还包含有其它功能,回退是不可能回退的。最终还是让我扛下所有,让我去兼容,好在还了我清白,这点儿工作量我就不计较了,反正就2行代码的事,在收到请求参数后作一个decode:
String params = request.getQueryString();
String decode = URLDecoder.decode(params);
// ignore
看到的同学觉得有用,点赞、关注、转发搞一个阔以不
路漫漫其修远兮,吾将上下而求索。——屈原《离骚》