(转)http长链接200万尝试及调优
原文在:http://rdc.taobao.com/blog/cs/?p=1062 其中讲到了不少TCP的调整参数,现转之,十分精彩 对于一个server,咱们通常考虑他所能支撑的qps,但有那么一种应用, 咱们须要关注的是它能支撑的链接数个数,而并不是qps,固然qps也是咱们须要考虑的性能点之一。这种应用常见于消息推送系统,也称为comet应用,好比聊天室或即时消息推送系统等。comet应用详细介绍可见我以前的介绍,在此很少讲。对于这类系统,由于不少消息须要到产生时才推送给客户端,因此当没有消息产生时,就须要hold住客户端的链接,这样,当有大量的客户端时,就须要hold住大量的链接,这种链接咱们称为长链接。 首先,咱们分析一下,对于这类服务,需消耗的系统资源有:cpu、网络、内存。因此,想让系统性能达到最佳,咱们先找到系统的瓶颈所在。这样的长链接,每每咱们是没有数据发送的,因此也能够看做为非活动链接。对于系统来讲,这种非活动链接,并不占用cpu与网络资源,而仅仅占用系统的内存而已。因此,咱们假想,只要系统内存足够,系统就可以支持咱们想达到的链接数,那么事实是否真的如此?若是真能这样,内核来维护这至关大的数据结构,也是一种考验。 要完成测试,咱们须要有一个服务端,还有大量的客户端。因此须要服务端程序与客户端程序。为达到目标,个人想法是这样的:客户端产生一个链接,向服务端发起一个请求,服务端hold住该链接,而不返回数据。 1. 服务端的准备 对于服务端,因为以前的假想,咱们须要一台大内存的服务器,用于部署nginx的comet应用。下面是我用的服务端的状况: ◦Summary: Dell R710, 2 x Xeon E5520 2.27GHz, 23.5GB / 24GB 1333MHz ◦System: Dell PowerEdge R710 (Dell 0VWN1R) ◦Processors: 2 x Xeon E5520 2.27GHz 5860MHz FSB (16 cores) ◦Memory: 23.5GB / 24GB 1333MHz == 6 x 4GB, 12 x empty ◦Disk-Control: megaraid_sas0: Dell/LSILogic PERC 6/i, Package 6.2.0-0013, FW 1.22.02-0612, ◦Network: eth0 (bnx2):Broadcom NetXtreme II BCM5709 Gigabit Ethernet,1000Mb/s ◦OS: RHEL Server 5.4 (Tikanga), Linux 2.6.18-164.el5 x86_64, 64-bit 服务端程序很简单,基于nginx写的一个comet模块,该模块接受用户的请求,而后保持用户的链接,而不返回。Nginx的status模块,可直接用于监控最大链接数。 服务端还须要调整一下系统的参数,在/etc/sysctl.conf中: ◦net.core.somaxconn = 2048 ◦net.core.rmem_default = 262144 ◦net.core.wmem_default = 262144 ◦net.core.rmem_max = 16777216 ◦net.core.wmem_max = 16777216 ◦net.ipv4.tcp_rmem = 4096 4096 16777216 ◦net.ipv4.tcp_wmem = 4096 4096 16777216 ◦net.ipv4.tcp_mem = 786432 2097152 3145728 ◦net.ipv4.tcp_max_syn_backlog = 16384 ◦net.core.netdev_max_backlog = 20000 ◦net.ipv4.tcp_fin_timeout = 15 ◦net.ipv4.tcp_max_syn_backlog = 16384 ◦net.ipv4.tcp_tw_reuse = 1 ◦net.ipv4.tcp_tw_recycle = 1 ◦net.ipv4.tcp_max_orphans = 131072 ◦ ◦/sbin/sysctl -p 生效 这里,咱们主要看这几项: net.ipv4.tcp_rmem 用来配置读缓冲的大小,三个值,第一个是这个读缓冲的最小值,第三个是最大值,中间的是默认值。咱们能够在程序中修改读缓冲的大小,可是不能超过最小与最大。为了使每一个socket所使用的内存数最小,我这里设置默认值为4096。 net.ipv4.tcp_wmem 用来配置写缓冲的大小。 读缓冲与写缓冲在大小,直接影响到socket在内核中内存的占用。 而net.ipv4.tcp_mem则是配置tcp的内存大小,其单位是页,而不是字节。当超过第二个值时,TCP进入pressure模式,此时TCP尝试稳定其内存的使用,当小于第一个值时,就退出pressure模式。当内存占用超过第三个值时,TCP就拒绝分配socket了,查看dmesg,会打出不少的日志“TCP: too many of orphaned sockets”。 另外net.ipv4.tcp_max_orphans这个值也要设置一下,这个值表示系统所能处理不属于任何进程的socket数量,当咱们须要快速创建大量链接时,就须要关注下这个值了。当不属于任何进程的socket的数量大于这个值时,dmesg就会看到”too many of orphaned sockets”。 另外,服务端须要打开大量的文件描述符,好比200万个,但咱们设置最大文件描述符限制时,会遇到一些问题,咱们在后面详细讲解。 2. 客户端的准备 因为咱们须要构建大量的客户端,而咱们知道,在一台系统上,链接到一个服务时的本地端口是有限的。因为端口是16位整数,也就只能是0到65535,而0到1023是预留端口,因此能分配的只是1024到65534,也就是64511个。也就是说,一台机器只能建立六万多个长链接。要达到咱们的两百万链接,须要大概34台客户端。 固然,咱们能够采用虚拟ip的方式来实现这么多客户端,若是是虚拟ip,则每一个ip能够绑定六万多个端口,34个虚拟ip就能够搞定。而我这里呢,正好申请到了公司的资源,因此就采用实体机来作了。 因为系统默认参数,自动分配的端口数有限,是从32768到61000,因此咱们须要更改客户端/etc/sysctl.conf的参数: ◦net.ipv4.ip_local_port_range = 1024 65535 ◦ ◦/sbin/sysctl -p 客户端程序是基于libevent写的一个测试程序,不断的创建新的链接请求。 3. 因为客户端与服务端须要创建大量的socket,因此咱们须要调速一下最大文件描述符。 客户端,须要建立六万多个socket,我设置最大为十万好了,的在/etc/security/limits.conf中添加: ◦admin soft nofile 100000 ◦admin hard nofile 100000 服务端,须要建立200万链接,那我想设置nofile为200万,好,问题来了。 当我设置nofile为200万时,系统直接没法登录了。尝试几回,发现最大只能设置到100万。在查过源码后,才知道,原来在2.6.25内核以前有个宏定义,定义了这个值的最大值,为1024*1024,正好是100万,而在2.6.25内核及其以后,这个值是能够经过/proc/sys/fs/nr_open来设置。因而我升级内核到2.6.32。ulimit详细介绍见博文:老生常谈: ulimit问题及其影响: http://blog.yufeng.info/archives/1380 升级内核后,继续咱们的调优,以下: sudo bash -c ‘echo 2000000 > /proc/sys/fs/nr_open’ 如今再设置nofile就能够了 ◦admin soft nofile 2000000 ◦admin hard nofile 2000000 4. 最后,在测试的过程当中,根据dmesg的系统打出的信息不断调整服务端/sbin/sysctl中的配置,最后咱们的测试完成了200万的长链接。 为了使内存占用尽可能减小,我将Nginx的request_pool_size从默认的4k改为1k了。另外,net.ipv4.tcp_wmem与net.ipv4.tcp_rmem中的默认值也设置成4k。 两百万链接时,经过nginx的监控获得数据: 两百万链接时系统内存状况: 5. 正常,咱们在线上配置时,Nginx的request_pool_size须要根据实际状况进行调整。net.ipv4.tcp_rmem 与net.ipv4.tcp_wmem的默认大小也须要调整。