技术分享| Linux 高并发踩过的坑及性能优化
Linux 操作系统是现在服务器的首选操作系统,在 Linux 的默认系统参数下,Linux 针对高并发的支持性并不是很好。小编从事 Linux 下应用程序开发多年,关于 Linux 系统下的高并发,小编自己踩过的坑,及如何解决踩过的坑下面列上几条,供大家参考,避免再次掉坑。
Linux 应用运行过程中出现 Too many open files 问题分析和解决
出现这句提示的原因是程序打开的文件 socket 连接数量超过系统设定值。
查看每个用户最大允许打开的文件数量
其中 open files (-n) 1024 表示每个用户最大允许打开的文件数量是 1024
当前系统文件句柄的最大数目,只用于查看,不能设置修改
查看某个进程的打开文件限制数
设置 open files 数值方法
这种设置方法在重启后会还原为默认值。
永久设置方法:
在最后加入
生效需要重启系统
这样修改之后,问题得到有效解决。
Linux 高并发下 time_wait 过多的问题分析及解决
现象是高并发场景下,服务器运行应用卡顿。
排查方法:查看服务器配置:
发现处于 time_wait 的数量太多,有几万条,应该是大量 socket 处于 TIME_WAIT 状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。TCP 连接状态描述:
TIME_WAIT 过多危害
网络情况不好时,如果主动方无 TIME_WAIT 等待,关闭前个连接后,主动方与被动方又建立起新的 TCP 连接,这时被动方重传或延时过来的 FIN 包过来后会直接影响新的 TCP 连接;同样网络情况不好并且无 TIME_WAIT 等待,关闭连接后无新连接,当接收到被动方重传或延迟的 FIN 包后,会给被动方回一个 RST 包,可能会影响被动方其它的服务连接。
针对如何解决 TIME_WAIT 过多这一问题,解答如下:
编辑内核文件/etc/sysctl.conf,加入以下内容:
然后执行 /sbin/sysctl -p 让参数生效.
简单来说,就是打开系统的 TIMEWAIT 重用和快速回收。
Linux 更多性能优化
如果您的系统的连接数本身就很多,如果以上配置调优后性能还不理想,可以再优化一下 TCP 的可使用端口范围,进一步提升服务器的并发能力。依然是/etc/sysctl.conf 文件中,加入下面这些配置:
Linux 内核更多参数优化说明
vim /etc/sysctl.conf
1、net.ipv4.tcp_max_syn_backlog = 65536
记录的那些尚未收到客户端确认信息的连接请求的最大值。对于超过 128M 内存的系统而言,缺省值是 1024,低于 128M 小内存的系统则是 128。
SYN Flood 攻击利用 TCP 协议散布握手的缺陷,伪造虚假源 IP 地址发送大量 TCP-SYN 半打开连接到目标系统,最终导致目标系统 Socket 队列资源耗尽而无法接受新的连接。为了应付这种攻击,现代 Unix 系统中普遍采用多连接队列处理的方式来缓冲(而不是解决)这种攻击,是用一个基本队列处理正常的完全连接应用(Connect()和 Accept() ),是用另一个队列单独存放半打开连接。
这种双队列处理方式和其他一些系统内核措施(例如 Syn-Cookies/Caches)联合应用时,能够比较有效的缓解小规模的 SYN Flood 攻击(事实证明<1000p/s)加大 SYN 队列长度可以容纳更多等待连接的网络连接数,一般遭受 SYN Flood 攻击的网站,都存在大量 SYN_RECV 状态,所以调大 tcp_max_syn_backlog 值能增加抵抗 syn 攻击的能力。
2、net.core.netdev_max_backlog = 32768
每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。
3、net.core.somaxconn = 32768
调整系统同时发起并发 TCP 连接数,可能需要提高连接储备值,以应对大量突发入局连接请求的情况。如果同时接收到大量连接请求,使用较大的值会提高受支持的暂挂连接的数量,从而可减少连接失败的数量。大的侦听队列对防止 DDoS 攻击也会有所帮助。挂起请求的最大数量默认是 128。
位置:/proc/sys/
4、net.core.wmem_default = 8388608
该参数指定了发送套接字缓冲区大小的缺省值(以字节为单位)
5、net.core.rmem_default = 8388608
该参数指定了接收套接字缓冲区大小的缺省值(以字节为单位)
6、net.core.rmem_max = 16777216
该参数指定了接收套接字缓冲区大小的最大值(以字节为单位)
7、net.core.wmem_max = 16777216
该参数指定了发送套接字缓冲区大小的最大值(以字节为单位)
8、net.ipv4.tcp_timestamps = 0
Timestamps 可以防范那些伪造的 sequence 号码。一条 1G 的宽带线路或许会重遇到带 out-of-line 数值的旧 sequence 号码(假如它是由于上次产生的)。时间戳能够让内核接受这种“异常”的数据包。这里需要将其关掉,以提高性能。
9、net.ipv4.tcp_synack_retries = 2
对于远端的连接请求 SYN,内核会发送 SYN+ACK 数据报,以确认收到上一个 SYN 连接请求包。这是所谓的三次握手(threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于 255,默认值是 5,对应于 180 秒左右时间。(可以根据 tcp_syn_retries 来决定这个值)
10、net.ipv4.tcp_syn_retries = 2
对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃。不应该大于 255,默认值是 5,对应于 180 秒左右时间。(对于大负载而物理通信良好的网络而言,这个值偏高,可修改为 2.这个值仅仅是针对对外的连接,对进来的连接,是由 tcp_retries1 决定的)
#net.ipv4.tcp_tw_len = 1
11、net.ipv4.tcp_tw_reuse = 1
表示开启重用,允许将 TIME-WAIT Sockets 重新用于新的 TCP 连接,默认为 0,表示关闭。这个对快速重启动某些服务,而启动后提示端口已经被使用的情形非常有帮助。
12、net.ipv4.tcp_mem = 94500000 915000000 927000000
tcp_mem 有 3 个 INTEGER 变量:low, pressure, high
low:当 TCP 使用了低于该值的内存页面数时,TCP 没有内存压力,TCP 不会考虑释放内存。(理想情况下,这个值应与指定给 tcp_wmem 的第 2 个值相匹配。这第 2 个值表明,最大页面大小乘以最大并发请求数除以页大小 (131072*300/4096)
pressure:当 TCP 使用了超过该值的内存页面数量时,TCP 试图稳定其内存使用,进入 pressure 模式,当内存消耗低于 low 值时则退出 pressure 状态。(理想情况下这个值应该是 TCP 可以使用的总缓冲区大小的最大值(204800*300/4096)
high:允许所有 TCP Sockets 用于排队缓冲数据报的页面量。如果超过这个值,TCP 连接将被拒绝,这就是为什么不要令其过于保守(512000*300/4096)的原因了。在这种情况下,提供的价值很大,它能处理很多连接,是所预期的 2.5 倍;或者使现有连接能够传输 2.5 倍的数据。
一般情况下这些值是在系统启动时根据系统内存数量计算得到的。
13、net.ipv4.tcp_max_orphans = 3276800
系统所能处理不属于任何进程的 TCP sockets 最大数量。假如超过这个数量﹐那么不属于任何进程的连接会被立即 reset,并同时显示警告信息。之所以要设定这个限制﹐纯粹为了抵御那些简单的 DoS 攻击﹐千万不要依赖这个或是人为的降低这个限制
14、net.ipv4.tcp_fin_timeout = 30
如果套接字由本端要求关闭,这个参数决定了它保持在 FIN-WAIT-2 状态的时间。对端可以出错并永远不关闭连接,甚至意外当机。缺省值是 60 秒。2.2 内核的通常值是 180 秒,你可以按这个设置,但要记住的是,即使你的机器是一个轻载的 WEB 服务器,也有因为大量的死套接字而内存溢出的风险,FIN-WAIT-2 的危险性比 FIN-WAIT-1 要小,因为它最多只能吃掉 1.5K 内存,但是它们的生存期长些。
15、net.ipv4.ip_conntrack_max = 10000
设置系统对最大跟踪的 TCP 连接数的限制(CentOS 5.6 无此参数)
同时还涉及到一个 TCP 拥塞算法的问题,你可以用下面的命令查看本机提供的拥塞算法控制模块:
对于几种算法的分析,详情可以参考下:TCP 拥塞控制算法的优缺点、适用环境、性能分析,比如高延时可以试用 hybla,中等延时可以试用 htcp 算法等。
如果想设置 TCP 拥塞算法为 hybla
对于内核版高于于 3.7.1 的,我们可以开启 tcp_fastopen:
Iptables 相关
如非必须,关掉或卸载 iptables 防火墙,并阻止 kernel 加载 iptables 模块。这些模块会影响并发性能。
IO 事件分配机制
在 Linux 启用高并发 TCP 连接,必须确认应用程序是否使用了合适的网络 I/O 技术和 I/O 事件分派机制。可用的 I/O 技术有同步 I/O,非阻塞式同步 I/O,以及异步 I/O。在高 TCP 并发的情形下,如果使用同步 I/O,这会严重阻塞程序的运转,除非为每个 TCP 连接的 I/O 创建一个线程。但是,过多的线程又会因系统对线程的调度造成巨大开销。因此,在高 TCP 并发的情形下使用同步 I/O 是不可取的,这时可以考虑使用非阻塞式同步 I/O 或异步 I/O。非阻塞式同步 I/O 的技术包括使用 select(),poll(),epoll 等机制。异步 I/O 的技术就是使用 AIO。
从 I/O 事件分派机制来看,使用 select()是不合适的,因为它所支持的并发连接数有限(通常在 1024 个以内)。如果考虑性能,poll()也是不合适的,尽管它可以支持的较高的 TCP 并发数,但是由于其采用“轮询”机制,当并发数较高时,其运行效率相当低,并可能存在 I/O 事件分派不均,导致部分 TCP 连接上的 I/O 出现“饥饿”现象。而如果使用 epoll 或 AIO,则没有上述问题(早期 Linux 内核的 AIO 技术实现是通过在内核中为每个 I/O 请求创建一个线程来实现的,这种实现机制在高并发 TCP 连接的情形下使用其实也有严重的性能问题。但在最新的 Linux 内核中,AIO 的实现已经得到改进)。
小结
综上所述,在开发支持高并发 TCP 连接的 Linux 应用程序时,应尽量使用 epoll 或 AIO 技术来实现并发的 TCP 连接上的 I/O 控制,这将为提升程序对高并发 TCP 连接的支持提供有效的 I/O 保证。
经过以上描述的优化配置之后,服务器的 TCP 并发处理能力会显著提高。上文所述配置仅供参考,用于生产环境请根据自己开发系统所部署的实际情况调整观察再调整。
参考文章:
版权声明: 本文为 InfoQ 作者【anyRTC开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/ad19fe0b4eadaa1e397836594】。文章转载请联系作者。
评论