标签归档:TCP

linux 百万并发连接之内核优化tcp_mem

在服务端,连接达到一定数量,诸如50W时,有些隐藏很深的问题,就不断的抛出来。 通过查看dmesg命令查看,发现大量TCP: too many of orphaned sockets错误,也很正常,下面到了需要调整tcp socket参数的时候了。

第一个需要调整的是tcp_rmem,即TCP读取缓冲区,单位为字节,查看默认值

  1. cat /proc/sys/net/ipv4/tcp_rmem
  2. 4096 87380 4161536

默认值为87380 byte ≈ 86K,最小为4096 byte=4K,最大值为4064K。

第二个需要调整的是tcp_wmem,发送缓冲区,单位是字节,默认值

  1. cat /proc/sys/net/ipv4/tcp_wmem
  2. 4096 16384 4161536

解释同上

第三个需要调整的tcp_mem,调整TCP的内存大小,其单位是页,1页等于4096字节。系统默认值:

  1. cat /proc/sys/net/ipv4/tcp_mem
  2. 932448 1243264 1864896

tcp_mem(3个INTEGER变量):low, pressure, high

  • low:当TCP使用了低于该值的内存页面数时,TCP不会考虑释放内存。
  • pressure:当TCP使用了超过该值的内存页面数量时,TCP试图稳定其内存使用,进入pressure模式,当内存消耗低于low值时则退出pressure状态。
  • high:允许所有tcp sockets用于排队缓冲数据报的页面量,当内存占用超过此值,系统拒绝分配socket,后台日志输出“TCP: too many of orphaned sockets”。

一般情况下这些值是在系统启动时根据系统内存数量计算得到的。 根据当前tcp_mem最大内存页面数是1864896,当内存为(1864896*4)/1024K=7284.75M时,系统将无法为新的socket连接分配内存,即TCP连接将被拒绝。

实际测试环境中,据观察大概在99万个连接左右的时候(零头不算),进程被杀死,触发out of socket memory错误(dmesg命令查看获得)。每一个连接大致占用7.5K内存(下面给出计算方式),大致可算的此时内存占用情况(990000 * 7.5 / 1024K = 7251M)。

这样和tcp_mem最大页面值数量比较吻合,因此此值也需要修改。

三个TCP调整语句为:

  1. echo “net.ipv4.tcp_mem = 786432 2097152 3145728”>> /etc/sysctl.conf
  2. echo “net.ipv4.tcp_rmem = 4096 4096 16777216”>> /etc/sysctl.conf
  3. echo “net.ipv4.tcp_wmem = 4096 4096 16777216”>> /etc/sysctl.conf

备注: 为了节省内存,设置tcp读、写缓冲区都为4K大小,tcp_mem三个值分别为3G 8G 16G,tcp_rmemtcp_wmem最大值也是16G。

目标达成

经过若干次的尝试,最终达到目标,1024000个持久连接。1024000数字是怎么得来的呢,两台物理机器各自发出64000个请求,两个配置为6G左右的centos测试端机器(绑定7个桥接或NAT连接)各自发出640007 = 448000。也就是 1024000 = (64000) + (64000) + (640007) + (64000*7), 共使用了16个网卡(物理网卡+虚拟网卡)。
终端输出

  1. ……
  2. online user 1023990
  3. online user 1023991
  4. online user 1023992
  5. online user 1023993
  6. online user 1023994
  7. online user 1023995
  8. online user 1023996
  9. online user 1023997
  10. online user 1023998
  11. online user 1023999
  12. online user 1024000

在线用户目标达到1024000个!

服务器状态信息

服务启动时内存占用:

  1. total used free shared buffers cached
  2. Mem: 10442 271 10171 0 22 78
  3. -/+ buffers/cache: 171 10271
  4. Swap: 8127 0 8127

系统达到1024000个连接后的内存情况(执行三次 free -m 命令,获取三次结果):

  1. total used free shared buffers cached
  2. Mem: 10442 7781 2661 0 22 78
  3. -/+ buffers/cache: 7680 2762
  4. Swap: 8127 0 8127
  5. total used free shared buffers cached
  6. Mem: 10442 7793 2649 0 22 78
  7. -/+ buffers/cache: 7692 2750
  8. Swap: 8127 0 8127
  9. total used free shared buffers cached
  10. Mem: 10442 7804 2638 0 22 79
  11. -/+ buffers/cache: 7702 2740
  12. Swap: 8127 0 8127

这三次内存使用分别是7680,7692,7702,这次不取平均值,取一个中等偏上的值,定为7701M。那么程序接收1024000个连接,共消耗了 7701M-171M = 7530M内存, 7530M*1024K / 1024000 = 7.53K, 每一个连接消耗内存在为7.5K左右,这和在连接达到512000时所计算较为吻合。
虚拟机运行Centos内存占用,不太稳定,但一般相差不大,以上数值,仅供参考。

执行top -p 某刻输出信息:

  1. top – 17:23:17 up 18 min, 4 users, load average: 0.33, 0.12, 0.11
  2. Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
  3. Cpu(s): 0.2%us, 6.3%sy, 0.0%ni, 80.2%id, 0.0%wa, 4.5%hi, 8.8%si, 0.0%st
  4. Mem: 10693580k total, 6479980k used, 4213600k free, 22916k buffers
  5. Swap: 8323056k total, 0k used, 8323056k free, 80360k cached
  6. PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
  7. 2924 yongboy 20 0 82776 74m 508 R 51.3 0.7 3:53.95 server

执行vmstate:

  1. vmstat
  2. procs ———–memory———- —swap– —–io—- –system– —–cpu—–
  3. r b swpd free buff cache si so bi bo in cs us sy id wa st
  4. 0 0 0 2725572 23008 80360 0 0 21 2 1012 894 0 9 89 2 0

获取当前socket连接状态统计信息:

  1. cat /proc/net/sockstat
  2. sockets: used 1024380
  3. TCP: inuse 1024009 orphan 0 tw 0 alloc 1024014 mem 2
  4. UDP: inuse 11 mem 1
  5. UDPLITE: inuse 0
  6. RAW: inuse 0
  7. FRAG: inuse 0 memory 0

获取当前系统打开的文件句柄:

  1. sysctl -a | grep file
  2. fs.file-nr = 1025216 0 1048576
  3. fs.file-max = 1048576

此时任何类似于下面查询操作都是一个慢,等待若干时间还不见得执行完毕。

  1. netstat -nat|grep -i “8000”|grep ESTABLISHED|wc -l
  2. netstat -n | grep -i “8000” | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

以上两个命令在二三十分钟过去了,还未执行完毕,只好停止。

小结

本次从头到尾的测试,所需要有的linux系统需要调整的参数也就是那么几个,汇总一下:

  1. echo “* – nofile 1048576” >> /etc/security/limits.conf
  2. echo “fs.file-max = 1048576” >> /etc/sysctl.conf
  3. echo “net.ipv4.ip_local_port_range = 1024 65535” >> /etc/sysctl.conf
  4. echo “net.ipv4.tcp_mem = 786432 2097152 3145728” >> /etc/sysctl.conf
  5. echo “net.ipv4.tcp_rmem = 4096 4096 16777216” >> /etc/sysctl.conf
  6. echo “net.ipv4.tcp_wmem = 4096 4096 16777216” >> /etc/sysctl.conf

其它没有调整的参数,仅仅因为它们暂时对本次测试没有带来什么影响,实际环境中需要结合需要调整类似于SO_KEEPALIVE、tcpmax_orphans等大量参数。

nginx+php-fpm,使用Unix Socket还是tcp方式连接?

将Nginx与FastCGI的通信方式由TCP改为Unix Socket。TCP在高并发访问下比Unix Socket稳定,但Unix Socket速度要比TCP快”,看来这是真的存在。两者各有优缺点啊

1.worker_processes 越大越好(一定数量后性能增加不明显)


2.worker_cpu_affinity 所有cpu平分worker_processes 要比每个worker_processes 都跨cpu分配性能要好;不考虑php的执行,测试结果worker_processes数量是cpu核数的2倍性能最优

3.unix domain socket(共享内存的方式)要比tcp网络端口配置性能要好
不考虑backlog,请求速度有量级的飞跃,但错误率超过50%
加上backlog,性能有10%左右提升

4.调整nginx、php-fpm和内核的backlog(积压),connect() to unix:/tmp/php-fpm.socket failed (11: Resource temporarily unavailable) while connecting to upstream错误的返回会减少
nginx:
配置文件的server块
listen 80 default backlog=1024;

php-fpm:
配置文件的
listen.backlog = 2048

kernel参数:
/etc/sysctl.conf,不能低于上面的配置
net.ipv4.tcp_max_syn_backlog = 4096
net.core.netdev_max_backlog = 4096

5.增加单台服务器上的php-fpm的master实例,会增加fpm的处理能力,也能减少报错返回的几率
多实例启动方法,使用多个配置文件:
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm.conf &
/usr/local/php/sbin/php-fpm -y /usr/local/php/etc/php-fpm1.conf &

nginx的fastcgi配置
    upstream phpbackend {
#      server   127.0.0.1:9000 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9001 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9002 weight=100 max_fails=10 fail_timeout=30;
#      server   127.0.0.1:9003 weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm1.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm2.sock weight=100 max_fails=10 fail_timeout=30;
      server   unix:/var/www/php-fpm3.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm4.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm5.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm6.sock weight=100 max_fails=10 fail_timeout=30;
#      server   unix:/var/www/php-fpm7.sock weight=100 max_fails=10 fail_timeout=30;
    }

        location ~ \.php* {
            fastcgi_pass   phpbackend;
#           fastcgi_pass   unix:/var/www/php-fpm.sock;
            fastcgi_index index.php;
       ……….
       }

6.测试环境和结果

内存2G
swap2G
cpu 2核 Intel(R) Xeon(R) CPU E5405  @ 2.00GHz
采用ab远程访问测试,测试程序为php的字符串处理程序


1)在开4个php-fpm实例,nginx 8个worker_processes 每个cpu4个worker_processes ,backlog为1024,php的backlog为2048,内核backlog为4096,采用unix domain socket连接的情况下,其他保持参数不变

性能和错误率较为平衡,可接受,超过4个fpm实例,性能开始下降,错误率并没有明显下降
结论是fpm实例数,worker_processes数和cpu保持倍数关系,性能较高
影响性能和报错的参数为
php-fpm实例,nginx worker_processes数量,fpm的max_request,php的backlog,unix domain socket


10W请求,500并发无报错,1000并发报错率为0.9%

500并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    4000 [#/sec] (mean) avg.
Time per request:       122.313 [ms] (mean)
Time per request:       0.245 [ms] (mean, across all concurrent requests)
Transfer rate:          800 [Kbytes/sec] received avg.

1000并发:
Time taken for tests:   25 seconds avg.
Complete requests:      100000
Failed requests:        524
   (Connect: 0, Length: 524, Exceptions: 0)
Write errors:           0
Non-2xx responses:      524
Requests per second:    3903.25 [#/sec] (mean)
Time per request:       256.197 [ms] (mean)
Time per request:       0.256 [ms] (mean, across all concurrent requests)
Transfer rate:          772.37 [Kbytes/sec] received

2)在其他参数不变,unix domain socket换为tcp网络端口连接,结果如下

500并发:
Concurrency Level:      500
Time taken for tests:   26.934431 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3712.72 [#/sec] (mean)
Time per request:       134.672 [ms] (mean)
Time per request:       0.269 [ms] (mean, across all concurrent requests)
Transfer rate:          732.37 [Kbytes/sec] received

1000并发:
Concurrency Level:      1000
Time taken for tests:   28.385349 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Requests per second:    3522.94 [#/sec] (mean)
Time per request:       283.853 [ms] (mean)
Time per request:       0.284 [ms] (mean, across all concurrent requests)
Transfer rate:          694.94 [Kbytes/sec] received

与1)比较,有大约10%的性能下降

7. 5.16调整fpm的max_request参数为1000,并发1000报错返回降到200个以下,
Transfer rate在800左右

HTTP / 3用UDP替换TCP以提高网络速度,可靠性

为站点和服务获得HTTP / 2的性能和安全性优势意味着进行体系结构更改,因为它颠覆了用于提高网站性能的分片等原则; 这可能就是为什么只有大约35%的网站目前使用HTTP / 2。

HTTP / 3加倍,提供非常相似的功能,但用UDP替换TCP。这一次可能需要对网络基础设施进行更根本的改变,以便利用比较差的连接和移动网络更好的性能,但对于大多数开发人员来说,这种改变将是透明的。

HTTP / 3是该协议的第三个主要版本,它包括TLS 1.3和一种称为QUIC(Quick UDP Internet Connection)的新传输协议; 根据最初由Google设计的2013协议,现在有多个贡献者和公司通过互联网工程任务组(IETF)参与其中

不可靠是一个机会

放弃HTTP一直用于UDP的TCP连接并不像看起来那么奇怪。U有时会扩展为“不可靠”而不是用户数据报协议,因为它不保证消息传递或数据包顺序。但是对于今天的网络,这实际上是一个提高HTTP / 2引入的多路复用连接性能的机会。“凭借HTTP / 3,我们将在同一个旧的不可靠互联网之上构建一个新的可靠协议,” Cloudflare首席技术官John Graham-Cumming告诉New Stack。

HTTP / 2通过在同一连接上发送多个HTTP请求,允许应用程序同时处理请求,从而更好地利用网络带宽。但只有在网络运行良好时才能实现这些收益。“你可以最大限度地提高吞吐量和带宽,因为TCP可以达到它可以达到的最大速度,并且所有并行连接都能以最快的速度运行,”Graham-Cumming说。

这很好,直到网络连接出现打嗝,例如网络拥塞或移动网络上从一个小区移动到另一个小区并且数据包丢失。“TCP保证发送数据包的顺序是应用程序接收的顺序 – 所以如果你错过了,那么一切都必须停止,直到特定数据包被重新传输。如果将多个请求复用到单个TCP连接上,则所有这些请求都必须停止并等待,即使丢失的数据包可能只影响其中一个。

Graham-Cumming解释说,这种“线路阻塞问题”是TCP固有的问题,使用UDP通过允许应用程序控制数据包的重传来修复它。“它可以说’数据包丢失,但它只影响这一个数据流,而其他数据流可以继续运行’。”这使得QUIC 在恶劣的网络条件下更加强大

HTTP / 3的连接迁移提议可以扩展在不同网络之间移动的稳健性,例如从Wi-Fi到移动宽带,这通常会因为IP地址的变化而破坏网络连接。“通过此提议,您和服务器之间的标识符将不是IP地址,而是协议本身内的标识符。这样您就可以在新网络上自动重启整个连接,这样您就可以无缝地从Wi-Fi转移到移动连接。原始互联网的一些假设是,存在相当固定的连接,我们现在正在进入一个非常移动的异构世界。“

Graham-Cumming解释说,HTTP / 3还直接集成了TLS,QUIC对数据包传输的额外控制也具有优势。“当事情通过互联网发送时,它们会被分解成数据包; 在TLS中,有一个传输数据缓冲区的概念。如果你想要真正有效率,你想要把所有这些事情排好; 这是一个请求,我将对它进行加密并通过互联网进行传输,以便一次性收到所有这些请求,并且不会分成较小的数据包,其中一些不会被延迟。如果您控制所有级别,如果您只是假设互联网是一种不可靠的发送数据包的方式,那么您可以控制发送内容的顺序,您可以控制加密它们的方式以及这些加密块如何传输。“

与HTTP服务器的初始连接将更快,因为HTTP / 3取出了通常需要建立连接的往返之一安全解释说,Mozilla首席工程师Martin Thomson是QUIC规范的编辑之一。

“使用TCP和TLS,连接设置以TCP握手开始:客户端发送SYN,服务器响应SYN + ACK,客户端使用ACK完成。然后在发送任何请求之前有一个TLS握手 – 它需要另一个类似的三个消息交换。QUIC在大多数情况下将其压缩到单个交换。一个关键特性是0-RTT,允许客户端立即发送请求; 这是TCP和TLS的一个选项,但你仍然需要等待TCP握手完成。“

默认安全

集成TLS还可以提高安全性,因为身份验证和加密是由网络协议提供的,而不是像TLS这样的高级协议提供的 – 而且在HTTP / 3中内置了TLS,使用它也不是可选的。Graham-Cumming指出:“业界正试图在默认情况下保证一切安全。” 当站点使用HTTPS时,浏览器现在会在站点没有加密连接时向您发出警告,而不是显示锁定HTTP / 3和QUIC是这个方向的另一个举措。

“更多的QUIC是加密的,”汤姆森解释道。这包括攻击者可能尝试使用的元数据。“除了一些有助于将数据包识别为QUIC数据包的比特之外,未加密的QUIC数据包的唯一部分是连接的不透明标识符。这包括TCP和TLS无法保护的内容,例如确认(’我从你那里得到5个字节’)。“

Thomson还认为加密实际上将简化部署QUIC。将新协议部署到需要更新的防火墙,路由器,NAT和其他网络设备上的复杂性阻碍了在浏览器中  创建新的,更有效的传输协议和延迟采用TLS 1.3的努力“这是部署QUIC的可能性的一部分。互联网往往会干扰新的协议,加密将保护QUIC免受干扰。“

他坚持认为,这是UDP是一个不错的选择的另一个原因。“新的协议不能直接部署在IP之上,如TCP或UDP,因为这需要更新的互联网。考虑更换或升级每个想要使用此新协议的房屋中的每个路由器的前景。UDP在这种情况下是理想的,因为它做得很少。它非常轻巧,因此我们可以在顶部构建所有必要的部件。UDP的主要缺点是已经显示阻止或降级UDP的少量网络。在极少数情况下,我们有HTTP / 2可以依靠。“

当用户访问站点时,他们的初始连接将通过HTTP或HTTP / 2,服务器将提供HTTP / 3作为替代; 了解提供该连接的标头的浏览器将记住它以供下次访问,但较旧的浏览器和设备将继续使用旧协议。格雷厄姆 – 卡明预测说:“我们预计HTTP / 2和1.1将会存在很长时间。”

网络变化

HTTP / 3可能有它的名字,但它仍在开发中; Graham-Cumming表示,情况稳定前几个月。尽管有一些HTTP / 3端点可用于测试Cloudflare等服务,但Facebook和Litespeed 已经证明了  2018年11月的HTTP / 3实现之间的互操作性,浏览器和Web服务器还没有支持HTTP / 3(Chrome的实验支持除外)对于谷歌的原始版本的QUIC)。

一旦他们这样做,服务提供商和托管公司将遵循,但可能需要更新旧的网络设备。“对于UDP的NAT遍历并不像TCP那样发达,一些家庭路由设备会假设UDP用于流媒体视频,而不是网络,”Graham-Cumming说。

Thomson说,如果你托管自己的网络服务器,你还有更多工作要做。“服务器管理员会发现QUIC需要的不仅仅是软件升级。启用UDP将需要网络和负载平衡基础架构的新支持。“网络运营商可能需要研究可以在软件中实现的更智能的第4层负载平衡解决方案,如Facebook的Katran项目

但由于HTTP / 3旨在解决HTTP / 2的问题,因此对HTTP / 2进行的优化(如域分片的更改)将在可用时自动应用。如果是这样的话,Graham-Cumming希望它能够推动网络的发展。“我们希望的是更快的网络下载和API。它应该使互联网更加可靠和快速,并且希望对于开发者来说,我们可以获得更丰富的API和Web应用程序,因为我们可以通过这种新协议更多地依赖互联网。

https://thenewstack.io/http-3-replaces-tcp-with-udp-to-boost-network-speed-reliability/