您的位置:首页 > 运维架构 > Linux

Linux 下 socket 高并发分析与优化

2015-11-23 20:06 543 查看
工作环境

操作系统:Linux ( Ubuntu server 12.04 / 64 bit)

内核版:Linux Ubuntu-Server 3.13.0-32-generic

1:连接限制

- 文件句柄限制

在 linux 下网络编程每一个 tcp 连接都要占一个文件描述符,一旦这个文件描述符使用完了,新的连接到来返回给我们的错误是“Socket/File:Can’t open so many files”。

这时需要明白操作系统对可以打开的最大文件数的限制。

进程限制

查看系统允许当前用户进程打开的文件数限制

ulimit -n
//默认输出 1024,说明对于一个进程而言最多只能打开1024个文件,所以如果采用此默认配置单进程最多可以并发上千个 TCP 连接。


1. 修改/etc/security/limits.conf文件,在文件中添加如下行:


* soft nofile 204800
* hard nofile 204800
// * 代表所有用户


2. 修改/etc/pam.d/login文件,在文件中添加如下行:


session required /lib/x86_64-linux-gnu/security/pam_limits.so
// 这是告诉Linux在用户完成系统登录后,应该调用pam_limits.so模块来设置系统对该用户可使用的各种资源数量
// 的最大限制(包括用户可打开的最大文件数限制),而pam_limits.so模块就会从/etc/security/limits.conf
// 文件中读取配置来设置这些限制值。修改完后保存此文件。


全局限制

查看Linux系统级的最大打开文件数限制,使用如下命令:

cat /proc/sys/fs/file-max
//显示的是这台Linux系统最多允许同时打开(即包含所有用户打开)文件数总和,是Linux系统级硬限制,
//所有用户级的打开文件数限制都不应超过这个数值。通常这个系统级硬限制是Linux系统在启动时根据系统硬件资源
//状况计算出来的最佳的最大同时打开文件数限制,如果没有特殊需要,不应该修改此限制,除非想为用户级打开文件
//数限制设置超过此限制的值。


cat /proc/sys/fs/file-nr
// 输出 9344 0 592026,分别为:1.已经分配的文件句柄数,2.已经分配但没有使用的文件句柄数,3.最大文件句柄数。但在kernel 2.6版本中第二项的值总为0,这并不是一个错误,它实际上意味着已经分配的文件描述符无一浪费的都已经被使用了 。


用 root 权限修改 /etc/sysctl.conf 文件,把这个数值改大些:

net.nf_conntrack_max = 204800
net.netfilter.nf_conntrack_max = 204800


/sbin/sysctl -p //立即生效
/sbin/sysctl net.netfilter.nf_conntrack_max // 验证是否生效


- 端口号限制

操作系统上端口号1024以下是系统保留的,从1024-65535是用户使用的。

如何标识一个TCP连接:系统用一个4四元组来唯一标识一个TCP连接:

{local ip, local port,remote ip,remote port}。

第一、二个参数代表了服务端的ip地址和端口号,第三、四个参数代表了客户端的ip地址和端口号。而我们作为服务端实际只使用了bind时这一个端口,说明端口号65535并不是并发量的限制。

server最大tcp连接数:server通常固定在某个本地端口上监听,等待client的连接请求。不考虑地址重用(unix的SO_REUSEADDR选项)的情况下,即使server端有多个ip,本地监听端口也是独占的,因此server端tcp连接4元组中只有remote ip(也就是client ip)和remote port(客户端port)是可变的,因此最大tcp连接为客户端ip数×客户端port数,对IPV4,不考虑ip地址分类等因素,最大tcp连接数约为2的32次方(ip数)×2的16次方(port数),也就是server端单机最大tcp连接数约为2的48次方。

2:内核优化

etc/sysctl.conf 是用来控制linux网络的配置文件,对于依赖网络的程序(如web服务器和cache服务器)非常重要。内核参数sysctl.conf的优化:

net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_mem = 786432 1048576 1572864
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 1200
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 262144
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.nf_conntrack_max = 204800 net.netfilter.nf_conntrack_max = 204800


另外,在此优化过程中可能会有报错:

error: "net.ipv4.ip_conntrack_max"is an unknown key
error: "net.ipv4.netfilter.ip_conntrack_max"is an unknown key
// 这个错误可能是你的防火墙没有开启或者自动处理可载入的模块ip_conntrack没有自动载入,解决办法有二,一是开启防火墙,二是自动处理开载入的模块ip_conntrack
modprobe nf_conntrack
echo "modprobe nf_conntrack">> /etc/rc.local


注:在内核2.6之前的此项防火墙配置需要改为

net.ipv4.ip_conntrack_max = 25000000
net.ipv4.netfilter.ip_conntrack_max = 25000000

modprobe ip_conntrack
echo "modprobe ip_conntrack">> /etc/rc.local


参数解释:

net.ipv4.tcp_max_tw_buckets = 6000
# 用来设定timewait的数量,默认是180 000,这里设为6000。
net.ipv4.ip_local_port_range = 1024 65000
# 用来设定允许系统打开的端口范围。
net.ipv4.tcp_tw_recycle = 1
# 用于设置启用timewait快速回收
net.ipv4.tcp_tw_reuse = 1
# 用于设置开启重用,允许将TIME-WAIT sockets重新用于新的TCP连接
net.ipv4.tcp_syncookies = 1
# 用于设置开启SYN Cookies,当出现SYN等待队列溢出时,启用cookies进行处理。
net.ipv4.tcp_synack_retries = 1
# 参数的值决定了内核放弃连接之前发送SYN+ACK包的数量。
net.ipv4.tcp_syn_retries = 1
# 表示在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.tcp_max_orphans = 262144
# 用于设定系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤立连接将立即
# 被复位并打印出警告信息。这个限制只是为了 防止简单的DoS攻击。不能过分依靠这个限制甚至人为减小这个值,
# 更多的情况下应该增加这个值。
net.ipv4.tcp_max_syn_backlog = 262144
# 用于记录那些尚未收到客户端确认信息的连接请求的最大值。对于有128MB内存的系统而言,此参数的默认值是
# 1024,对小内存的系统则是128。
net.ipv4.tcp_rmem = 4096 87380 16777216
# TCP读buffer
net.ipv4.tcp_wmem = 4096 65536 16777216
# TCP写buffer
net.ipv4.tcp_mem = 786432 1048576 1572864
# 同样有3个值,意思是:net.ipv4.tcp_mem[0]:低于此值,TCP没有内存压力。
# net.ipv4.tcp_mem[1]:在此值下,进入内存压力阶段。
# net.ipv4.tcp_mem[2]:高于此值,TCP拒绝分配socket。
net.ipv4.tcp_timestsmps = 0
# 时间戳可以避免序列号的卷绕。
net.ipv4.tcp_fin_timeout = 30
# 决定了套接字保持在FIN-WAIT-2状态的时间。默认值是60秒。正确设置这个值非常重要,有时即使一个负载很小的
# Web服务器,也会出现大量的死套接字而产生内存溢出的风险。
net.ipv4.tcp_keepalive_time = 1200
# 表示当keepalive启用的时候,TCP发送keepalive消息的频度。默认值是2(单位是小时)。1200/60 = 20 分钟
net.core.somaxconn = 65535
# 默认值是128, 这个参数用于调节系统同时发起的tcp连接数,在高并发的请求中,默认的值可能会导致链接超时或
# 者重传,因此,需要结合并发请求数来调节此值。
net.core.netdev_max_backlog = 262144
# 表示当每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许发送到队列的数据包的最大数目。
net.core.rmem_default = 8388608
# 默认的接收窗口大小
net.core.wmem_default = 8388608
# 默认的发送窗口大小
net.core.rmem_max = 16777216
# 最大socket接收缓冲
net.core.wmem_max = 16777216
# 最大socket发送缓冲
net.nf_conntrack_max = 204800
# 提升系统整体连接数
net.netfilter.nf_conntrack_max = 204800
# 提升系统整体连接数


/sbin/sysctl -p //立即生效
echo "/sbin/sysctl -p">> /etc/rc.local //重启后自动生效
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: