近些年来,随着微服务系统大热,docker云容器部署已经是不可忽略的话题。笔者负责的产品线应用本来是部署在虚拟机上,公司统一要求产品线应用所有迁移到docker云容器中。在迁移过程当中,遇到了客户端请求丢失的问题,在此梳理总结一下供读者参考,以避免出现一样的问题。 java
本文来自于个人博客网站http://51think.net,欢迎来访。nginx
一、POS终端会发起支付请求,业务码为SDK040,服务端60秒内没有给予响应,请求超时。
二、请求超时后,POS终端自动发起查询请求,业务码为SDK041,服务端返回错误信息“订单不存在”。
这个问题只是几率性出现,出现几率约为千分之一,经过压测也没有复现这个问题。web
咱们经过splunk日志查询发现有以下规律:
一、订单不存在的报错以前就存在,量比较少,没有报障。
二、上云以后,订单不存在问题激增,客户投诉。
三、9月26号切5%的量到云容器,错误量上涨三倍左右。
四、10月17号切40%的量到云容器,错误量上涨5至10倍。
五、10月22号下午将量所有迁回虚拟机,错误量回归至以往正常水平。
以下截图展现了,一个时间段内报有“订单不存在”错误日志的统计规律,即上云以前错误量少,上云以后(10月17日-10月22日)错误量明显增多: redis
从上述的问题规律中能够看出,订单不存在的错误量在上云以后明显增长。经过代码分析得知,后台报订单不存在的缘由在于数据表里没有落地本次订单交易。经过订单号查询日志,发现SDK040的支付请求根本就没有到达服务端应用,更别谈插入数据库。咱们把怀疑点瞄向整个网络传输路径,多是哪一个传输过程当中把请求拒绝了。先看一下网络路径: docker
从图中能够看出,POS智能机进行支付时须要通过netscaler设备,而后进入内部代理。nginx是原有的虚机代理,ingress是k8s提供的代理组件,pod是k8s中承载web应用的容器,在本案中和tomcat同级。在进行云容器迁移时,咱们经过netscaler切量,将虚机的流量逐步引流到pod中。图中是已经切量了40%到云容器环境中。因为“订单不存在”错误在应用层没有任何日志记录,咱们到nginx和ingress日志上看看可否找到一些蛛丝马迹,两者的access日志都是这个样式的: 数据库
因为POS机客户端都是POST请求,到达nginx层和ingress层的access日志只能看到请求的URI部分,看不到请求参数,只能看到终端的IP信息,没法根据订单号来锁定具体的请求记录,因此咱们只能根据终端的IP来查询指定时间范围内有没有请求,结果是使人失望的,问题订单的支付请求到达nginx或者ingress 。tomcat
请求没有到达nginx或者ingress,意味着三种可能:
一、客户端POS机器没有发出请求。服务器
咱们经过客户端远程抓包,发现客户端的确发了请求包,并且还报了socketException错误。问题不可能出如今客户端,由于netscaler切量行为是发生在服务器端,一切量错误数就增长,客户端并无作任何升级改造。
二、网络抖动致使丢包。cookie
在本例中应该与网络抖动没有关系,netscaler切量不可能致使外部网络抖动。
三、netscaler转发请求是否出了问题。网络
咱们找了IDC相关同事一块儿排查,IDC同事通过抓包,告知咱们说支付请求没有到达netscaler。
那这个支付请求究竟跑哪去了,问题一度陷入僵局。系统组对比过ingress和nginx的配置,说出一个区别就是nginx配置了会话保持sticky session,而ingress没有配置。这个差别最终被我否决了,由于咱们的服务端提供的接口都是无状态的,后台会话信息所有共享在redis,并不依赖会话保持。
咱们再仔细思考一下整个问题过程,还有一个小规律:切量到ingress,错误数会增长,回切到nginx,错误数不会当即降低,而是缓慢降低,持续两个小时左右,错误数才能恢复到以往正常水平。这一点感受不合理,若是切量出现的问题,那么切回去应该当即恢复才对,而不是再等个两个小时,这里面确定有必定的联系。
大胆质疑:会不会是, 切量后让整个链路带有必定的特性,而后这个特性会携带到回切后的链路中?而且这个特性持续了两个小时以后才消失?这两个链路的交集是POS机,咱们又再次分析了POS机的代码,POS机的收银台功能是安卓写的,http请求使用了httpclient的工具包。
一、http请求的cookie信息
一开始,我觉得是cookie在做怪,由于httpclient默认请求的时候会携带上次请求返回的cookie信息,然而,POS端的http调用代码是忽略cookie的,以下:
如图中红框配置项所示,即每次请求都是独立的,并不会携带任何cookie信息,线索中断。
二、http请求的header信息
cookie疑问已无望,咱们转眼关注两者的header信息差别。咱们在内网环境中,直接经过curl -I -X 命令来访问ingress和nginx,看看两者返回的头信息有何差别。
ingress返回的header信息:
nginx返回的header信息:
经过上图发现了一个明显差别,nginx比ingress多了一个配置项keep-Alive:timeout=10,这个配置项的意思是让这个连接保持10秒。而ingress使用的http1.1没有这个配置项,意味着这个连接会默认保持两个小时。两个小时?是否是和以前回切以后错误持续时间等同?嗯,就是这个缘由!另外,咱们联系了netscaler的厂商,厂商还透露了一个重要信息,netscaler上有个空闲连接时间管理,配置的是180秒,即netscaler会关闭空闲180秒的连接。至此,咱们再还原一下问题场景:
切量到ingress后,POS的请求路径是netscaler>ingress>pod,因为ingress没有配置keep-Alive:timeout=10,pos客户端使用httpclient默认保持连接两个小时,而此时netscaler到达了空闲连接阈值180秒,netscaler主动关闭此连接,而POS端的httpclient仍然认为此连接能够复用,再次使用此连接发起请求时,netscaler认为此连接已关闭,会拒绝转发此请求。即便切回nginx,此问题依然会持续一段时间,不会当即消失。
将ingress增长keep-Alive:timeout=10配置项,从新切量,问题解决。在此也要提醒一下你们:一、谨慎使用httpclient。在本例中,POS做为客户端彻底没有必要使用httpclient进行链接池管理,能够采用底层java提供的URLConnection来创建短链接,使用完毕后当即关闭,这样也不会带来连接管理的相关问题。二、中间件替换时要关注各个配置项的差别性。nginx和ingress的这个配置项keep-Alive:timeout=10差别被公司的系统组同事忽略了,认为有connection:keep-Alive配置项就行。后期若是遇到相似奇葩问题,必定要全方位对比各个配置项,不能有缺漏。