把整篇文章拆分成四个模块, 这四个模块分别对应着带宽瓶颈所涉及的四个故障层级, 第一个层级是TCP握手阻塞, 第二个层级是连接池打满, 第三个层级是队列溢出雪崩, 第四个层级是隐性丢包。
首先抛出一个与大家通常认知相反的观点, 也就是非常识性的结论, 那就是带宽不够用的情况并不是单纯地表现为“网站运行速度慢”, 实际上是服务器系统在没有任何提示的情况之下, 直接将第N加1个用户请求直接丢弃掉了, 该用户所看到的景象并不是延迟导致的慢, 而是白屏显示、操作超时以及403错误代码的出现情形, 这种关键性的不同点决定了你们进行问题排查的整体方向会完全不一样, 因为百分之九十的人群在启动排查工作的第一步阶段就已经查错了需要关注的层级范围。
从底层的逻辑来看, Nginx listen backlog的默认值是128。当带宽达到瓶颈时, SYN加ACK的回传延迟会从2毫秒增加到20毫秒以上。握手的窗口时间会从大约12秒压缩到大约1.5秒以下。与此同时, 队列会在短时间内就被填满。
设定的量化红线标准是, 当在ss -s这一命令的输出内容里面, timewait这个状态的占比超过了百分之三十五的这个数值时, 就会直接触发该预警或动作。
在操作过程中, 需要执行命令sysctl net.ipv4.tcp_max_syn_backlog, 并将其值设定为4096。同时, 参数
这里需要特别说明的是, 这两个参数在安装或者配置时必须进行同步调整, 缺一不可, 如果只修改了其中某一个参数的设置情况, 那么这种单独修改的行为是没有任何实际意义的, 相当于没有进行修改一样无效。
进行验证工作, 也就是在压测环境里模拟两千并发请求的情况, 然后把P99握手时间小于八毫秒定为一个合格的标准线。如果实际测试结果超过了这个数值, 就不应该先去考虑增加带宽, 而是应当先着手扩大backlog这个参数, 之后再谈论是否需要增加带宽。
所谓的底层逻辑就是, 下游数据库和Redis连接池的默认大小是200。网络带宽出现抖动, 会导致TCP重新发送数据包, 使得单个连接的往返时间从12毫秒突然增加到45毫秒。
这等效于只有58个连接还能正常工作, 第59个请求就不得不进入排队状态。虽然这个过程并不报错, 但在用户看来, 系统就是出现了卡住的现象。
量化红线:连接等待队列>0且持续15s,判死。
操作建议是: 需要把HikariCP的maximumPoolSize的上限设定为CPU核数乘以2再加上磁盘数的数值。千万不要随随便便拍脑袋决定, 也不要认为设置得大一点就更安全, 因为数据量过大会反而拖慢垃圾回收的效率。
我们需要进行验证, 具体的情况是看到Arathas中的thread命令抓取到了BLOCKED线程, 并且数量大于10个, 这时候应当考虑增加连接池的大小或者添加只读实例, 而不应选择扩大带宽。
这是最致命的, 也是最为人们所忽略的一种情形。当网络带宽被彻底占满之后, 就会导致内核中的softirq处理不过来相应的任务, 于是就会出现在netstat -s命令的输出结果里, dropped这一项的数据开始发生跳动。
关键的地方不在于到底丢失了多少个数据包, 而是丢包的动作去触发了应用层次的重传操作, 而重传操作本身又在消耗带宽资源, 这样一来就形成了一个正向反馈的控制环路, 这个环路在三十秒之内的时间长度里把整个系统从原本比较缓慢的状态一路推倒变成了完全瘫痪无法使用的状态。
在/proc/net/snmp里面, 只要RcvbufErrors大于等于0而且秒级里面的增量超过了50这个数, 就不需要继续等着监控发出报警的提示了。
操作:tc qdisc add dev eth0 root netem loss 0.1%做故障注入,验证业务熔断阈值<5s。
完成验证工作, 具体规则如下: 当注入故障后如果订单接口的5xx错误比例低于0.1%, 这说明熔断功能已经正常生效;假如该错误比例高于1%, 则意味着你制定的降级预案形同虚设, 没有任何实际作用, 需要对此进行重新编写。
带宽的使用量并没有达到百分之百的水平, 但是网络中出现了一点点丢包情况, 丢包的比率小于百分之零点零一, 这时候TCP协议里的重传阈值这个指标没有被触发, 连接往返时延RTT看起来一切正常, 监控面板上显示的都是绿色正常的状态, 但是在TLS加密协议的握手阶段如果丢失了一个数据包, 那就必须把整个网络连接重建一遍, 这相当于把有效利用的带宽额度又强行砍掉百分之四十, 这样的层面上的丢包情况, 在你的监控仪表盘上是完全看不见踪迹的。
量化的红线标准是, 通过ss -ti命令来查看, 只要发现其中的reordering计数值不是零, 那就属于异常情况, 即使这个数值仅仅只有3。
在进行操作的时候, 针对使用API网关和支付回调等构成的TLS密集场景, 将tls1.3max_initial_fly参数值, 原本设置为10的状态, 调整为了20的状态, 通过这样一种增加冗余量的方式, 来达到抵御微小丢包的目的。
进行验证, 具体操作是开启开关进行对照, 并持续运行10分钟, 如果首字节的延迟差超过30毫秒, 就确认命中了此处说的这一层。
关于认知纠偏这一概念, 有一个非常关键的点需要明确, 那就是带宽不足这种状况, 它的首要表现从来都不是所谓的速度变慢, 而是会让人感到十分丢人, 因为丢失的是第N加1个用户的连接, 与此同时监控系统上显示的却是一片正常的状态, 面对这种情况, 你往往会倾向于去增加CPU的资源或者是去检查代码, 但这样一来你的判断方向全都是错误的, 完全偏离了问题的核心所在。
有实际证据显示, 在2024年的第三个季度, 某电商企业做了压测记录, 当带宽利用率到了78%的时候, P99指标还算是正常的, 但当到了82%的时候, timewait的值就超过了40%, 等到带宽利用率达到85%的时候, 就触发了级联雪崩现象, 系统从还可以接受的状态变成功能全坏的状态, 只用了11秒钟时间, 这期间没有发出预警信号,也没有出现逐渐变慢的过程。
具体要做的步骤是今晚执行ss -s | grep timewait这条命令, 如果发现timewait的状态占比超过了35%, 那么就按照模块01里面的说明来操作。但是如果是API请求非常密集, 或者TLS握手次数特别多的那种情况, 就直接跳过中间的过程进入模块04进行相应处理。
要是还拿不准到底是哪一层网络协议在造成瓶颈拖慢速度。请将netstat -s以及ss -ti的输出内容粘贴出来, 逐项进行对照检查, 在15分钟之内定位具体的网络层级。
相关标签: