我正在深入地下,或者您应该了解的有关优化网络应用程序的知识

问候朋友!

在前两篇文章( )中,我们陷入了技术选择之间的复杂性,并在Ostrovok.ru中为我们的解决方案寻找了最佳设置。 今天我们要提出什么主题?

每个服务应在某个服务器上工作,并通过操作系统的工具与硬件进行通信。 这些工具很多,还有它们的设置。 在大多数情况下,它们的默认设置将绰绰有余。 在本文中,我想谈谈标准设置还不够的情况,而我必须更深入地了解操作系统-在我们的Linux中



我们明智地使用内核


上一篇文章中,我讨论了Haproxy中cpu-map 选项 。 使用它,我们将Haproxy进程绑定到双处理器服务器上一个内核的线程。 我们为网卡中断处理提供了第二个核心。

下面是一个屏幕,您可以在其中看到类似的分隔。 左侧是Haproxy在user space中占用的内核,而右侧是通过处理kernel space中断来kernel space



使用此功能可以自动将中断绑定到网卡
Bash脚本:
 #! /bin/bash interface=${1} if [ -z "${interface}" ];then echo "no interface specified" echo "usage: ${0} eth1" exit 1 fi nproc=$(grep 'physical id' /proc/cpuinfo|sort -u|wc -l) ncpu=$(grep -c 'processor' /proc/cpuinfo) cpu_per_proc=$[ncpu / nproc] queue_threads=$[cpu_per_proc / 2] binary_map="" cpumap="" for(( i=0; i < ncpu; i++ ));do cpumap=${cpumap}1 b+='{0..1}' done binary_map=($(eval echo ${b})) ###         ###    ,      . ethtool -L ${interface} combined ${queue_threads} || true count=${ncpu} while read irq queue;do let "cpu_num=$[count-1]" let "cpu_index=$[2**cpu_num]" printf "setting ${queue} to %d (%d)\n" $((2#${binary_map[${cpu_index}]})) ${cpu_num} printf "%x\n" "$((2#${binary_map[${cpu_index}]}))" > /proc/irq/${irq}/smp_affinity [ ${interface} != ${queue} ] && count=$[count-1] [ $[ncpu - count] -gt ${queue_threads} ] && count=${ncpu} done < <(awk "/${interface}/ {if(NR > 1){ sub(\":\", \"\", \$1); print \$1,\$(NF)} }" /proc/interrupts) exit 0 


Internet上有许多合适的简单和更复杂的脚本可以完成相同的工作,但是此脚本足以满足我们的需求。

在Haproxy,我们从第一个内核开始将进程链接到内核。 相同的脚本从最后一个开始绑定中断。 因此,我们可以将服务器处理器分为两个阵营。

为了更深入地了解中断和网络,我强烈建议您阅读本文

我们揭示网络设备的功能


碰巧一次有很多帧可以在网络上飞过,并且即使有机会这样做,卡队列也可能尚未准备好迎接这种来宾。

让我们谈谈网卡缓冲区。 大多数情况下,默认值不会使用整个可用缓冲区。 您可以使用功能强大的ethtool实用程序查看当前设置。

命令用法示例:

 > ethtool -g eno1 Ring parameters for eno1: Pre-set maximums: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096 Current hardware settings: RX: 256 RX Mini: 0 RX Jumbo: 0 TX: 256 

现在,让我们从生活中汲取一切:

 > ethtool -G eno1 rx 4096 tx 4096 > ethtool -g eno1 Ring parameters for eno1: Pre-set maximums: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096 Current hardware settings: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096 

现在,您可以确定该卡没有受到约束并且可以最大程度地发挥其功能。

最小的sysctl设置以获得最大的收益


Sysctl提供了您可以想象的所有颜色和大小的多种选择。 而且,通常,Internet上有关优化问题的文章涵盖了这些参数中相当令人印象深刻的部分。 在我们的案例中,我将只考虑那些对改变确实有用的东西。

net.core.netdev_max_backlog-从网卡获取帧的队列,然后由内核处理。 凭借快速的接口和大量的流量,它可以快速填满。 默认值 :1000
我们可以通过查看/ proc / net / softnet_stat文件中的第二列来观察该队列的剩余部分。
 awk '{print $2}' /proc/net/softnet_stat 

该文件本身描述了系统中每个CPU的每行netif_rx_stats的结构。
具体而言,第二列描述处于丢弃状态的数据包数量。 如果第二列中的值随时间增长,那么可能值得增加net.core.netdev_max_backlog的值或使CPU更快。

net.core.rmem_default / net.core.rmem_max && net.core.wmem_default / net.core.wmem_max-这些参数指示套接字读取和写入缓冲区的默认值/最大值。 可以在创建套接字时在应用程序级别更改默认值(顺便说一下,Haproxy具有执行此操作的参数 )。 我们遇到过这样的情况:内核抛出的数据包超过了Haproxy设法耙开的数据包,然后问题开始了。 因此,事情很重要。

net.ipv4.tcp_max_syn_backlog-负责限制尚未建立的新连接的接收SYN数据包。 如果有大量新连接(例如,来自Connection: close的许多HTTP请求),则提高此值很有意义,以免浪费时间发送转发的数据包。

net.core.somaxconn-在这里,我们讨论的是已建立的连接,但尚未由应用程序处理。 如果服务器是单线程服务器,并且有两个请求,则第一个请求将由accept()函数处理,第二个请求将挂在backlog ,后者的大小负责此参数。

nf_conntrack_max可能是所有参数中最著名的。 我认为几乎所有处理iptables的人都知道这一点。 当然,理想情况下,如果不需要使用伪装的iptables,则可以卸载conntrack模块,而不用考虑它。 就我而言,使用的是Docker ,因此您不会上传任何特殊内容。

监控方式 很明显,不是很明显


为了避免盲目搜索“代理速度变慢”的原因,建立一些图表并将其与触发器重叠将非常有用。

nf_conntrack_count是最明显的指标。 在它上面,您可以监视conntrack表中现在有多少个连接。 当表溢出时,新连接的路径将关闭。

当前值可以在这里找到:

 cat /proc/sys/net/netfilter/nf_conntrack_count 



重传的TCP分段 - 分段传输的次数。 该指标非常庞大,因为它可以讨论不同级别的问题。 传输量的增加可能表明存在网络问题,需要优化系统设置,甚至可能表明最终软件(例如Haproxy)没有发挥作用。 尽管如此,此值的异常增长可能会成为进行诉讼的原因。



在我们国家,价值增加通常表示供应商之一存在问题,尽管服务器和网络的性能均存在问题。

验证示例:

 netstat -s|grep 'segments retransmited' 

套接字Recv-Q-记住,我们谈论的时刻是应用程序可能没有足够的时间来处理请求,然后socket backlog会增加吗? 该指标的增长清楚地表明应用程序有问题,无法解决。

当Haproxy中的maxconn参数具有默认值(2000),并且它根本不接受新连接时,我在具有该度量的图形中看到了山脉。

再举一个例子:

 ss -lntp|awk '/LISTEN/ {print $2}' 



具有按TCP连接状态进行细分的图不会是多余的:



并分别渲染time-wait/established ,因为 通常,它们的值与其余值有很大不同:



除了这些指标以外,还有许多其他指标,但更为明显-例如,网络接口或CPU上的负载。 他们的选择将更多地取决于您的工作负载。

而不是结论


通常,仅此而已-我试图描述设置http反向代理时必须面对的关键点。 看起来任务并不困难,但是随着负载的增加,总是在错误的时间出现的陷阱也增加了。 我希望本文能帮助您避免遇到我遇到的困难。

一切和平

Source: https://habr.com/ru/post/zh-CN440782/


All Articles