前言重印:https://www.cnblogs.com/alchemystar/p/13409534.html
原来我有Linux果仁源代码确实有很大的好处,尤其是在处理问题时。当你看到报告错误的时刻,你可以在脑海中闪现现象/原因/和解决方案。即使是一些边缘现象也能很快反映出原因。作者阅读了LinuxTCP协议栈的一些源代码,在解决以下问题时有一种非常流畅的感觉。
漏洞站点首先,这个问题不难解决,但由这个问题引起的现象非常有趣。首先描述这种现象。作者将对自行开发的Dubbo协议隧道网关进行压力测试(该网关的设计也非常有趣,准备在以后的博客中发布)。让我们首先看看下压力测量的拓扑结构:
为了测试作者网关的独立性能,两端只保留一个网关,即网关1和网关2。当压力达到一定水平时,它开始报告错误,导致压力测量停止。很自然地,人们会认为门户无法承载它。
关口情况我去了gateway2机器,没有发现任何错误。Gateway1有大量的502报告错误。502是坏网关,Nginx我想到的第一件事是,gateway2在上游被Nginx击溃并踢出。
然后,让我们首先看看网关2的负载,并检查监控。发现网关2在4核8g机器上只使用一个核,根本不存在瓶颈。有IO问题吗?在研究了小而差的网卡流量后,这个猜测被打消了。
Nginx机器的CPU利用率接近100%这时,一个有趣的现象被发现。Nginx真的是个CPU高手!
再次进行压力测试。我走到Nginx的机器顶部,发现Nginx的四个工人分别占据了一个内核,并吃掉了CPU-#-!
什么,自称有权势的Nginx这么软弱。据说事件驱动的epolledge触发纯C?一定是姿势不对!
没有摆脱Nginx直接沟通的压力既然投机是Nginx的瓶颈,那我们就除掉Nginx吧。网关1和网关2直接连接,压力测试TPS上升。此外,gateway2的CPU在没有压力的情况下最多消耗两个内核。
去Nginx那里读日志由于Nginx的机器权限不在作者手中,我一开始没有注意他的日志。现在联系相应的运维部门查看。在访问日志中发现了大量502个错误报告,这确实是Nginx的。我再次查看了错误日志,发现了大量错误
无法分配请求的地址
因为我已经阅读了TCP源代码,所以我会立即做出反应。对端口号筋疲力尽的由于Nginx的上游和后端AndyLau默认为短连接,因此当大量请求流量进入uWait连接时,会产生大量时间。
这些时间uwait占用端口号,使用大约需要1分钟内核回收
cat/proc/sys/net/ipv4/ip本地端口范围
三十二亿七千六百八十六万一千
也就是说,在一分钟的等待时间内生成28232(61000-32768)次Socket这将导致端口号耗尽,即470.5tps(28232/60),这只是一个容易达到的压力测量值。事实上,这种限制是在客户端,服务器服务器上没有这样的限制,因为服务器端口号只有一个著名的端口号,比如8080。在上游,Nginx扮演客户端,网关2扮演Nginx
为什么Nginx的CPU是100%作者很快就弄明白了Nginx为什么会吃掉机器的CPU,问题出在端口号的搜索过程中。
让我们来看一下性能消耗最大的函数:
int_;inet_;hash_;connect(…)
{
//请注意,这是静态变量
静态u32提示;
//提示有助于搜索不是从0,而是从下一个要分配的端口号
u32偏移量=提示+端口偏移量;
.....
inet_uuu获取uuu本地uuuuu端口uuuuu范围(&low,&高);
//剩下的是61000-32768
剩余=(高-low)+1
......
对于(i=1;i<;=剩余;i++){
端口=low+(I+偏移量)%剩余;
/*你占领check吗*/
....
goto:好;
}
.......
好啊:
提示+=i;
......
}
查看上面的代码,如果没有可用的端口号,则需要循环剩余时间来声明端口号已用尽,即28232次。然而,在正常情况下,由于提示的存在,每次搜索都从要分配的下一个端口号开始,并且可以通过单位数搜索找到端口号。如下图所示:
因此,当端口号耗尽时,Nginx的工作进程沉浸在上述for循环中,无法自拔,耗尽了CPU。
为什么gateway1打电话给Nginx没有问题很简单,因为作者在网关1呼叫Nginx时设置了Keepalive,所以采用了长连接,所以这个端口号的使用没有限制。
如果Nginx身后有多台机器因为端口号搜索会导致100%的CPU,并且每当有可用的端口号时,由于提示,搜索时间可能是1和28232之间的差异。
因为端口号限制是针对特定的远程服务器:端口。因此,只要Nginx的Backend有多台机器,甚至在同一台机器上有多个不同的端口号,Nginx就不会有任何压力,只要它不超过临界点。
增加端口号范围当然,更明智的方案是增加端口号的范围,这样它就可以抵抗更多的等待时间同时,TCP_uMax_uTW_JetLi,将其关闭,TCP_max_tw_JetLi是内核中等待次数最多的时间,只要端口范围-TCP_max_tw_Li大于某个值,就会始终有一个可用端口,当临界值再次增加时,可以避免继续破坏临界点。
cat/proc/sys/net/ipv4/ip_uuu本地uu端口uu范围
二十二亿七千六百八十六万一千
cat/proc/sys/net/ipv4/tcp_uu-max_uu-tw_uu-Li-Jet-s
二万
打开TCP_utw_uu_u;reuse
这个问题小马事实上,有一个TCP解决方案tw_u;reuse这个参数。
echo和#39;1'>;/proc/sys/net/ipv4/tcp_utw_uoreuse
事实上,时间——过度等待的原因是其恢复时间实际上需要1分钟。这1min实际上是TCP协议中指定的2msl时间,在Linux中固定为1min。
#定义TCP_uutimewait_uulen(60*HZ)/*销毁等待时间的等待时间
*州,大约60秒*/
2msl的原因是为了消除剩余数据包对新的相同五元组Socket的影响,也就是说,在2msl(1min)内重用这个五元组是有风险的。为了解决这个问题,Linux采取了一系列措施来防止这种情况,使得在大多数情况下1秒以内的等待时间可以重复使用。下面的代码检测这次是否重用了wait。
__inet_uu散列_uuu连接
|->;_check成立
静态int_________;inet_____;check__
{
......
/*首先检查等待时间套接字*/
每个(sk2、节点和头->;twchain)的sk_uNulls{
tw=伊内特·twsk(SK2);
//如果您在等待中找到匹配的端口,您可以判断它是否可重用
如果(INET_TW_)匹配(sk2,net,hash,acookie,
saddr,daddr,港口,DIF){
如果(twsk独特(SK、SK2、twp))
goto独特;
其他的
goto并非独一无二;
}
}
......
}
其核心功能是twsk独特的,其判断逻辑如下:
inttcp_u…Marx_u…独特(…)
{
......
如果(tcptw->;tw_ts_近期_邮票&&
(twp==null|(sysctl_tcp_twliuYifei)&&
获取秒数()-tcptw->;最近的邮票;1))){
//写入uux将SEQ设置为SNDuunxt+65536+2
//这确保了在数据传输速率<=下,它不会以80mbit/s的速率被重绕
tp->;写入uq=tcptw->;tw_uSND_nxt+65535+2
......
返回1;
}
返回0;
}
上述代码的逻辑如下:
当TCP打开时Timestamp和TCPtw_u对于reuse来说,在connect中搜索端口时,您只需要使用该端口比之前的时间uZiyi的等待状态记录的最新时间戳>;1s,您可以重新使用此端口,将之前的1分钟缩短为1s。同时,为了防止潜在的序列号冲突,在65537中增加了writedirectly_uq,这样当单个Socket的传输速率小于80mbit/s时,就不会造成序列号重叠(冲突)。同时,这个tw_uts_uuu最近的uuu设置时间如下图所示:
因此,如果Socket输入时间u,那么如果在等待状态u之后发送相应的数据包,则此时间将受到影响,即等待对应的端口可用的时间。开启该参数后,由于该参数从1分钟缩短为1秒,Nginx的单到单上游平价TPS从470.5TPS(28232/60)跃升至28232TPS,增长了60倍。如果性能不够,可以添加上述端口号范围,TCP_uMax_uTW_JetLi向下调整并继续改善TPS,但TCP_max_tw_JetLi的减少可能会有序列号重叠的风险。毕竟,Socket没有经过2msl阶段就被重用了。
不要打开TCP_utw_uu循环打开TCP_utw_uuSongZuying此参数将对NAT环境产生很大影响。建议不要打开它。
Nginx从上游转向增长连接事实上,上述一系列问题是由Nginx与Backend的短暂关系造成的。Nginx从1.1.4开始,实现了后端机的长连接支持功能。此功能可在上游配置中启用:
上游后端{
服务器127.0.0.1:8080;
#需要特别注意的是,keepalive指令并没有限制nginx工作进程可以打开的上游服务器的连接总数。connections参数应该设置为一个足够小的数字,以便上游服务器也可以处理新的传入连接。
保持32岁;
保持生命\uuuu30秒超时;#将后端连接的最大空闲时间设置为30秒
}
这样一来,前端和后端是长连接,每个人都可以再次快乐地玩。
由此产生的风险点单个远程IP:端口耗尽将导致CPU已满。因此,Nginx在配置上游时需要格外小心。假设PE扩大了一个Nginx的容量。为了防止出现问题,我们应该首先配置一个AndyLau来查看情况。此时,如果数量相对较大,崩溃临界点会导致大量错误(应用程序本身没有压力,毕竟临界值是470.5tps(28232/60)),即使是在同一个Nginx上请求非域名也不会响应,因为CPU耗尽。配置更多Backend/打开TCP_uTW_uFei可能是一个不错的选择。
无论应用程序有多强大,它仍然是在内核中进行的,永远无法摆脱Linux内核的束缚。因此,优化Linux核本身的参数是非常有意义的。如果你读过一些内核源代码,它无疑会帮助我们解决在线问题,并指导我们避免一些陷阱!
最新评论