Linux 的内核配置调优
早期的系统,系统资源包括 CPU、内存等都是非常有限的,系统为了保持公平,默认要限制进程对资源的使用情况。由于 Linux 的默认内核配置无法满足要求,因此需要对其进行适当的调优。
我们可以通过 ulimit 查看一下典型的机器默认的限制情况:
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 204800
max locked memory (kbytes, -l) 32
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 10240
cpu time (seconds, -t) unlimited
max user processes (-u) 204800
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
比如其中的 open files,默认一个进程能打开的文件句柄数量为 1024,对于一些需要大量文件句柄的程序,如 web 服务器、数据库程序等,1024 往往是不够用的,在句柄使用完毕的时候,系统就会频繁出现 emfile 错误。
俗话说:一个巴掌拍不响,要完成 C100K 的目标,需要服务器端与客户端的紧密配合,下面将分别对这二者的调优进行介绍。
客户端
1:文件句柄数量受限
在 Linux 平台上,无论是编写客户端程序还是服务端程序,在进行高并发 TCP 连接处理时,由于每个 TCP 连接都要创建一个 socket 句柄,而每个 socket 句柄同时也是一个文件句柄,所以其最高并发数量要受到系统对用户单一进程同时可打开文件数量的限制以及整个系统可同时打开的文件数量限制。
1.1:单一进程的文件句柄数量受限
我们可以 ulimit 命令查看当前用户进程可打开的文件句柄数限制:
[root@localhost ~]# ulimit -n
1024
这表示当前用户的每个进程最多允许同时打开 1024 个文件,除去每个进程必然打开的标准输入、标准输出、标准错误、服务器监听 socket、进程间通讯的 unix 域 socket 等文件,剩下的可用于客户端 socket 连接的文件数就只有大概 1024-10=1014 个左右。也就是说,在默认情况下,基于 Linux 的通讯程序最多允许同时 1014 个 TCP 并发连接。
对于想支持更高数量的 TCP 并发连接的通讯处理程序,就必须修改 Linux 对当前用户的进程可同时打开的文件数量的软限制(soft limit)和硬限制(hardlimit)。其中:
软限制是指 Linux 在当前系统能够承受的范围内进一步限制用户能同时打开的文件数。
硬限制是指根据系统硬件资源状况(主要是系统内存)计算出来的系统最多可同时打开的文件数量。
通常软限制小于或等于硬限制,可通过 ulimit 命令查看软限制和硬限制:
[root@localhost ~]# ulimit -Sn
1024
[root@localhost ~]# ulimit -Hn
4096
修改单一进程能同时打开的文件句柄数有 2 种方法:
1、直接使用 ulimit 命令,如:
[root@localhost ~]# ulimit -n 1048576
执行成功之后,ulimit n、Sn、Hn 的值均会变为 1048576。但该方法设置的值只会在当前终端有效,且设置的值不能高于方法 2 中设置的值。
2、对 /etc/security/limits.conf
文件,添加或修改:
* soft nofile 1048576
* hard nofile 1048576
其中, *
代表对所有用户有效,若仅想针对某个用户,可替换星号。
- soft 即软限制,它只是一个警告值。
- hard 代表硬限制,是一个真正意义的阈值,超过就会报错。
- nofile 表示打开文件的最大数量。
1048576 = 1024 * 1024,为什么要取这个值呢?因为在 linux kernel 2.6.25 之前通过 ulimit -n(setrlimit(RLIMIT_NOFILE)) 设置每个进程的最大打开文件句柄数不能超过 NR_OPEN(1024*1024),也就是 100 多 w(除非重新编译内核),而在 25 之后,内核导出了一个 sys 接口可以修改这个最大值(/proc/sys/fs /nr_open),具体的 changelog 在 https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=9cfe015aa424b3c003baba3841a60dd9b5ad319b
注意文件保存之后,需要注销或重启系统方能生效。
1.2:整个系统的文件句柄数量受限
解决完单一进程的文件句柄数量受限问题后,还要解决整个系统的文件句柄数量受限问题。我们可通过以下命令查看 Linux 系统级的最大打开文件数限制:
[root@localhost ~]# cat /proc/sys/fs/file-max
98957
file-max 表示系统所有进程最多允许同时打开的文件句柄数,是 Linux 系统级硬限制。通常,这个系统硬限制是 Linux 系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制,如果没有特殊需要,不应该修改此限制。
要修改它,需要对 /etc/sysctl.conf
文件,增加一行内容:
fs.file-max = 1048576
保存成功后,需执行下面命令使之生效:
[root@localhost ~]# sysctl -p
2:端口数量受限
解决完文件句柄数量受限的问题后,就要解决 IP 端口数量受限的问题了。一般来说,对外提供请求的服务端不用考虑端口数量问题,只要监听某一个端口即可。可客户端要模拟大量的用户对服务端发起 TCP 请求,而每一个请求都需要一个端口,为了使一个客户端尽可能地模拟更多的用户,也就要使客户端拥有更多可使用的端口。
由于端口为 16 进制,即最大端口数为 2 的 16 次方 65536(0-65535)。在 Linux 系统里,1024 以下端口只有超级管理员用户(如 root)才可以使用,普通用户只能使用大于等于 1024 的端口值。
我们可以通过以下命令查看系统提供的默认的端口范围:
[root@localhost ~]# cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000
即只有 61000-32768=28232 个端口可以使用,即单个 IP 对外只能同时发送 28232 个 TCP 请求。
修改方法有以下 2 种:
1、执行以下命令:
echo "1024 65535"> /proc/sys/net/ipv4/ip_local_port_range
该方法立即生效,但重启后会失效。
2、修改 /etc/sysctl.conf 文件,增加一行内容:
net.ipv4.ip_local_port_range = 1024 65535
保存成功后,需执行下面命令使之生效:
[root@localhost ~]# sysctl -p
修改成功后,可用端口即增加到 65535-1024=64511 个,即单个客户端机器只能同时模拟 64511 个用户。要想突破这个限制,只能给该客户端增加 IP 地址,这样即可相应成倍地增加可用 IP:PORT 数。具体可参考 yongboy 的这篇文章。
服务端
1:文件描述符数量受限
同客户端的问题 1。
2:TCP 参数调优
要想提高服务端的性能,以达到我们高并发的目的,需要对系统的 TCP 参数进行适当的修改优化。
方法同样是修改 /etc/sysctl.conf 文件,增加以下内容:
net.ipv4.tcp_tw_reuse = 1
当服务器需要在大量 TCP 连接之间切换时,会产生大量处于 TIME_WAIT 状态的连接。TIME_WAIT 意味着连接本身是关闭的,但资源还没有释放。将 net_ipv4_tcp_tw_reuse 设置为 1 是让内核在安全时尽量回收连接,这比重新建立新连接要便宜得多。
net.ipv4.tcp_fin_timeout = 15
这是处于 TIME_WAIT 状态的连接在回收前必须等待的最小时间。改小它可以加快回收。
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
提高 TCP 的最大缓冲区大小,其中:
net.core.rmem_max:表示接收套接字缓冲区大小的最大值(以字节为单位)。
net.core.wmem_max:表示发送套接字缓冲区大小的最大值(以字节为单位)。
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
提高 Linux 内核自动对 socket 缓冲区进行优化的能力,其中:
net.ipv4.tcp_rmem:用来配置读缓冲的大小,第 1 个值为最小值,第 2 个值为默认值,第 3 个值为最大值。
net.ipv4.tcp_wmem:用来配置写缓冲的大小,第 1 个值为最小值,第 2 个值为默认值,第 3 个值为最大值。
net.core.netdev_max_backlog = 4096
每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。默认为 1000。
net.core.somaxconn = 4096
表示 socket 监听(listen)的 backlog 上限。什么是 backlog 呢?backlog 就是 socket 的监听队列,当一个请求(request)尚未被处理或建立时,他会进入 backlog。而 socket server 可以一次性处理 backlog 中的所有请求,处理后的请求不再位于监听队列中。当 server 处理请求较慢,以至于监听队列被填满后,新来的请求会被拒绝。默认为 128。
net.ipv4.tcp_max_syn_backlog = 20480
表示 SYN 队列的长度,默认为 1024,加大队列长度为 8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_syncookies = 1
表示开启 SYN Cookies。当出现 SYN 等待队列溢出时,启用 cookies 来处理,可防范少量 SYN 攻击,默认为 0,表示关闭。
net.ipv4.tcp_max_tw_buckets = 360000
表示系统同时保持 TIME_WAIT 套接字的最大数量,如果超过这个数字,TIME_WAIT 套接字将立刻被清除并打印警告信息。默认为 180000。
net.ipv4.tcp_no_metrics_save = 1
一个 tcp 连接关闭后,把这个连接曾经有的参数比如慢启动门限 snd_sthresh、拥塞窗口 snd_cwnd,还有 srtt 等信息保存到 dst_entry 中,只要 dst_entry 没有失效,下次新建立相同连接的时候就可以使用保存的参数来初始化这个连接。
net.ipv4.tcp_syn_retries = 2
表示在内核放弃建立连接之前发送 SYN 包的数量,默认为 4。
net.ipv4.tcp_synack_retries = 2
表示在内核放弃连接之前发送 SYN+ACK 包的数量,默认为 5。
完整的 TCP 参数调优配置如下所示:
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.netdev_max_backlog = 4096
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 20480
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_tw_buckets = 360000
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2
其它一些参数
vm.min_free_kbytes = 65536
用来确定系统开始回收内存的阀值,控制系统的空闲内存。值越高,内核越早开始回收内存,空闲内存越高。
vm.swappiness = 0
控制内核从物理内存移出进程,移到交换空间。该参数从 0 到 100,当该参数=0,表示只要有可能就尽力避免交换进程移出物理内存;该参数=100,这告诉内核疯狂的将数据移出物理内存移到 swap 缓存中。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论