如何解决dubbo流量上线时的非平滑问题


如何解决dubbo流量上线时的非平滑问题,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。笔者最近解决了一个困扰了业务系统很久的问题。这个问题只在发布时出现,每次只影响一两次调用,相较于其它的问题来说,这个问题有点不够受重视。由于种种原因,使得这个问题到了业务必须解决的程度,于是就到了笔者的手上。我们采用的是dubbo服务,这是个稳定成熟的RPC框架。但是我们在某些应用中会发现,只要这个应用一发布(或者重启),就会出现请求超时的问题,如下图所示:
而且都是第一笔请求会报错,之后就再也没有问题了。好了,现象我们知道了,于是开始排查那个时间点的日志。Server端没有任何日志,而Client(App1)端报错超时。报错如下所示:看日志报错是这个tcp五元组(1.1.1.1:44502->2.2.2.2:20880)有问题。于是笔者netstat了一下,查看当前此连接的状态:这个连接处于正常的ESTABLISHED状态,而且调用2.2.2.2这个server的连接只有这一个,那后续这台机器调用2.2.2.2这个server肯定只用到了这个连接,查看日志发现除了这一笔,其它调用一切正常。按照上面的报错,肯定这个连接有问题,按照正常思路,是否是第一笔调用的时候各种初始化过程(以及jit)导致server处理请求过慢?如下图所示:
但这个问题很快被第二笔请求(和第一笔请求间隔只有一秒)给否决了。也就是说第一笔请求花了60s还超时,而第二笔请求在第一笔请求发出后的一秒后发出就成功了(而且耗时是毫秒级的)。如果按照上述思路,第二笔请求也应该等各种初始化完成之后再处理,也应该卡相当长的一段时间才对。再加上server端没有任何日志,就感觉好像没有收到请求。于是我们开始了思路2,Client端没有发送成功。由于Dubbo是基于Netty的,其请求传输的处理过程是个NIO的异步化过程(只不过通过使用future机制对业务来说是同步的)。首先我们看下dubbo的超时检测机制,如下图所示:
当然,还有future的超时机制,这边我就不列举出来了。从这个机制可以看出,就算Client端发送的时候(因为写是写到socket的writebuffer里面,一般不会满,所以不会报错)没有发送出去,也不会在发的那一刻报出错误,而是要等定时扫描。为了验证这个猜想,笔者就开始着手排查连接的日志。因为规律是第一笔失败,那么笔者开始怀疑连接创建的有问题,但后面的请求成功又表明连接创建是没有问题的。那么,是否是连接还没有创建好就发送了第一笔请求呢?带着这样的疑问,笔者找到了如下的日志:由日志所示,1.1.1.1:44502这个连接串是在2019-08-22 20:32:51.876连接成功的,而上面出错的日志起始时间为请求在连接成功建立之前,感觉非常符合笔者上述的猜想。但细想又不对,上述日志表示的是在2019-08-22 20:32:51.876开始建立连接并在1ms内建立成功连接。而请求确是50s发出的,dubbo本身代码不可能在连接还没做出创建动作的时候就开始发送请求(因为44502是由kernel分配的,没到创建连接动作之前是不可能知道这个端口号的,但它却在日志里面打印出来了),如下图所示:
思考了一段时间,笔者觉得这种情况很有可能是日志是不准确的,它仅仅打印当前连接包装类中的信息,之前那个出错的连接已经被新建的连接掩盖了(在日志中)。我们看下dubbo中对应的打印日志代码:这个1.1.1.1:44502是由channel.getLocalAddress()得出的,我们看下调用链:笔者看了下代码,dubbo在reconnect(重新连接的时候)会把nettyClient.channel换掉,从而可能打印出来和发送请求时不同的client端口信息。再加上日志里面打印出来的线程号DubboClientReconnectTimer-thread-1就几乎可以确定,之前还有一个连接,只不过这个连接失败了,新建的连接为1.1.1.1f :44502。再仔细的找了下日志,发现了下面这条日志:即是说dubbo在把1.1.1.1:44471关闭后,立马创建了2.2.2.2:44502 那么整体过程如下图所示:
我们看下1.1.1.1:44471是什么时候创建的,继续搜寻日志:发现其在而且笔者翻看了业务日志,发现连接1在其生命周期内有且只有一笔请求,这笔请求就是报错的请求,由此可以看出这个连接1从建立成功开始就无法使用。首先由于dubbo是基于netty的,这种成熟的广泛应用的框架在创建连接这种问题上不应该会有问题。 而笔者仔细翻看netty翻看netty源码也确实遵守了NIO的编写套路。那么问题可能出现在环境上,既然应用(client/server)本身日志找不到更多的有用信息,那么看看操作系统能否有什么可以追寻的蛛丝马迹。于是笔者首先用dmesg查看下kernel打印的日志,client端没发现什么有用的信息,但server端的dmesg引起了笔者的注意其字面意思为kenerl本身可能在20880端口遭到了SYN泛洪攻击,发送cookies。 这种日志会在并发连接过多的情况下由kernel打印出来。笔者netstat了下频繁出问题的机器,发现其特点都是连接特别多(达到了好几千甚至上万)。而dubbo的上线机制是只要注册到zookeeper后,zookeeper给所有的机器推送,所有的机器立马连接,这样就会造成比较高的并发连接,如下图所示:
既然怀疑到了高并发connect,那么笔者就立马用netstat看下kernel对于tcp的统计信息:果然有这种并发连接导致的统计信息,socket overflowed表示的是server端的tcp_backlog队列溢出了。到这里笔者有八成把握是这个tcp_backlog设置的过小的问题了。下面就是分析,tcp_backlog溢出为什么会导致上述连接虽然成功但是第一笔报错的现象,从而和日志相印证,来证明笔者的猜测。先从tcp的三次握手进行着手分析,从tcp_v4_do_rcv函数进行入手,因为这个函数是tcp协议获取packet的地方, 香港云主机当三次握手的SYN第一次进来的时候,应该会走这个函数:我们继续分析conn_request函数,由于里面很多逻辑,所以我们滤掉了很多细节:上面就是三次握手的前两次握手,如下图所示:
现在让我们看下最关键的第三次握手(客户端发给server ack的过程),依旧从tcp_v4_do_rcv开始上面这些代码片段主要表明的就是如果三次握手成功,会在server端把新创建的server端sock放到inet_csk_reqsk里面,然后由后面的tcp_child_process去处理。 上面分析完大致的tcp三次握手代码逻辑之后,我们现在看看造成本文问题现象的核心分支tcp_check_req,这个是在对应的req被放到inet_csk_reqsk(即不需要cookie或者cookie校验成功)后才能执行到这一句,源码如下:如果进了listen_overflow分支,server端会有两种现象,一个是直接将acked设置为1(仅设置标识不发送ack),另一个是发送ack。这两个分支都会把对应的req从reqsk_queue中删除。下图就是走了listen_overflow后不同分之,三次握手的情况:不设置tcp_abort_on_overflow 设置tcp_abort_on_overflow
由上面看出设置了tcp_abort_on_overflow会在三次握手之后立马置为closed状态,二不设置tcp_abort_on_overflow会等第一个请求发出来的时候才会知道这个连接其实已经被server端丢弃了。看上面的代码,即在syn_recv_sock返回null的时候,会到listen_overflow,对应的tcp协议的函数为:sk_acceptq_is_full里面判断full的队列长度是下面这三个参数中的最小值:限于篇幅,就不给出这里判断队列是否为full的源码了。一开始笔者仅仅在server端设置了下面这三个参数(天真的以为dubbo考虑到了backlog,设置了一个较大的数值,其实其用了jdk默认的50)发现依旧有Close old netty client这样的错误日志,但是业务方确告诉笔者已经没有上线流量损失的情况了。笔者上去观察了一下情况,发现没有流量损失是由于如上图所述reset报文立马发回来,导致能够出错的时间窗口缩小为2s,而不是之前的第一笔必报错,2s内由于没有任何请求,所以大大减少了出错概率。如下图所示:
而创建连接和close old netty client日志的间隔都是小于等于两秒也证明了tcp_abort_on_overflow生效了由于dubbo用的是jdk默认的backlog(50),所以笔者在dubbo源码对应的部分做了调整,如下代码所示:将这个修改过的dubbo版本给业务方(server端)用了以后,再也没有对应的报错日志了,dubbo上线也平滑了。事实上,从开始分析问题到猜测要调整backlog所花费的时间并不长。但是笔者喜欢把所有的细节和自己的猜想一一印证,通过看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注开发云行业资讯频道,感谢您对开发云的支持。

相关推荐: nginx+php-fpm服务HTTP状态码502怎么解决

这篇文章主要介绍“nginx+php-fpm服务HTTP状态码502怎么解决”,在日常操作中,相信很多人在nginx+php-fpm服务HTTP状态码502怎么解决问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”nginx+php…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

Like (0)
Donate 微信扫一扫 微信扫一扫
Previous 08/12 20:20
Next 08/12 20:20

相关推荐