1.每个 TCP 连接占用多少内存 ?
由于TCP 连接在建立时并不会真的去分配接收缓冲区和发送缓冲区,所以不能通过简单看缓冲区.
那TCP连接最少占多少呢?
从逻辑上说,通过文件描述符 sockfd 找到对应的 tcp_sock 的途径是:
current->files->fdt->fd[sockfd]->private_data->sk。
这里每个 struct 的大小可以通过 /proc/slabinfo 找到,本文以陈硕家某台机器上运行的 Debian 8 x86-64 为例(Linux 内核版本 3.16):
| struct | size | slab cache name |
| ---------------- | ---- | ------------------ |
| file | 256 | "filp" |
| dentry | 192 | "dentry" |
| socket_alloc | 640 | "sock_inode_cache" |
| tcp_sock | 1792 | "TCP" |
| socket_wq | 64 | "kmalloc-64" |
| inet_bind_bucket | 64 | "tcp_bind_bucket" |
| epitem | 128 | "eventpoll_epi" |
| tcp_request_sock | 256 | "request_sock_TCP" |
因此,每个 TCP socket 占用的内存最少是 256 + 192 + 640 + 1792 + 64 = 2944 字节。后面的实验表明,实际占用的字节数会比这个略大 .(SLAB等因素)
实际测试结果 创建 10000 个 TCP socket 会使用 31552 KB 内存(通过比较 /proc/meminfo 得出),即每个 TCP socket 占用 3.155 KB,这个数字很接近上面考虑 SLAB overhead 之后的计算结果。
2.最大文件描述符数
由于linux一切为文件,所以TCP连接也是一个fd(文件描述符),linux设置了一定的上限,当需要支持高并发的时候,将要修改其上限.
查看用户级文件描述符数限制的方法:
$ ulimit -n
临时设置用户级文件描述符数限制:
$ ulimit -SHn max-file-number
-H 设置硬资源限制,硬资源限制用于控制软限制。限定一旦设置只有root用户可以增加硬限制,普通用户只能减少自己的硬限制大小。
-S 设置弹性资源限制,弹性限制用于限制具体的用户或者进程。设置后普通用户可以增加,但是不能超过硬限制大小。 如果不指定-S或者-H,那么弹性资源限制和硬限制将同时设置。
soft nofile (软限制)是指Linux在当前系统能够承受的范围内进一步限制用户同时打开的文件数.
hard nofile (硬限制)是根据系统硬件资源状况(主要是系统内存)计算出来的系统最多可同时打开的文件数量 通常软限制小于或等于硬限制.
永久设置用户级文件描述符数限制,需要在/etc/security/limits.conf文件中加入如下两项:
* hard nofile max-file-number
* soft nofile max-file-number
第一行是指系统的硬限制,第二行是软限制. * 表示所有用户,也可以指定具体的用户名.
临时设置系统级文件描述符数限制:
sysctl -w fs.file-max = max-file-number
永久设置系统级文件描述符数限制,需要在/etc/sysctl.conf文件中添加如下一项:
fs.file-max = max-file-number
最后执行sysctl -p 命令使更改生效.
# 查看所有用户打开文件数的最大限制(此值与硬件配置有关)
cat /proc/sys/fs/file-max
# 限制
所有进程打开的文件描述符数不能超过/proc/sys/fs/file-max
单个进程打开的文件描述符数不能超过user
limit中nofile的soft limit
nofile的hard limit不能超过/proc/sys/fs/nr_open
#读写缓存限制
在/etc/sysctl.conf中,可以调整优化
fs.file-max = 999999
net.ipv4.tcp_rmem = 4096 4096 16777216
net.ipv4.tcp_wmem = 4096 4096 16777216
我们同样修改文件/etc/security/limits.conf允许所有用户打开999,999个文件描述。
3.内核参数
/proc/sys/fs/file-max , 系统级文件描述符数限制(临时).
一般修改/proc/sys/fs/file-max后,应用程序需要把/proc/sys/fs/inode-max设置为新/proc/sys/fs/file-max值的3-4倍,否则导致i节点不够用.
/proc/sys/fs/epoll/max_user_watches , 一个用户能够往epoll内核事件表中注册的事件的总量.它是指该用户打开的所有epoll实例总共能监听的事件数目,而不是单个epoll实例能监听的事件数目.往epoll内核事件表中注册一个事件,在32位系统上大概消耗90字节的内核空间,在64位系统上则消耗160字节的内核空间.所以,这个内核参数限制了epoll使用的内核内存总量.
/proc/sys/net/core/somaxconn , 制定listen监听队列里,能够建立完整连接从而进入ESTABLISHED状态的最大数目.
/proc/sys/net/ipv4/tcp_max_syn_backlog , 指定listen监听队列里,能够转至ESTABLISHED或者SYS_RCVD状态的最大数目.
/proc/sys/net/ipv4/tcp_wmem , 它包含3个值,分别指定一个socket的TCP写缓冲区的最小值,默认值和最大值.
/proc/sys/net/ipv4/tcp_syncookies , 指定是否打开TCP同步标签(syncookie).同步标签通过启动cookie来防止一个监听socket因不停地重复接收来自同一个地址的连接请求(同步报文段),而导致listen监听队列溢出(所谓的SYN风暴).
修改之后都需要执行sysctl -p 命令使更改生效.
4.内核参数sysctl.conf的优化
/etc/sysctl.conf 是用来控制linux网络的配置文件,对于依赖网络的程序(如web服务器和cache服务器)非常重要,RHEL默认提供的最好调整。
推荐配置(把原/etc/sysctl.conf内容清掉,把下面内容复制进去):
net.ipv4.ip_local_port_range = 1024 65536
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.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_sack = 0
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_no_metrics_save=1
net.core.somaxconn = 262144
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
5.参数说明
net.core.netdev_max_backlog = 400000
#该参数决定了,网络设备接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。
net.core.optmem_max = 10000000
#该参数指定了每个套接字所允许的最大缓冲区的大小
net.core.rmem_default = 10000000
#指定了接收套接字缓冲区大小的缺省值(以字节为单位)。
net.core.rmem_max = 10000000
#指定了接收套接字缓冲区大小的最大值(以字节为单位)。
net.core.somaxconn = 100000
#Linux kernel参数,表示socket监听的backlog(监听队列)上限
net.core.wmem_default = 11059200
#定义默认的发送窗口大小;对于更大的 BDP 来说,这个大小也应该更大。
net.core.wmem_max = 11059200
#定义发送窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
#严谨模式 1 (推荐)
#松散模式 0
net.ipv4.tcp_congestion_control = bic
#默认推荐设置是 htcp
net.ipv4.tcp_window_scaling = 0
#关闭tcp_window_scaling
#启用 RFC 1323 定义的 window scaling;要支持超过 64KB 的窗口,必须启用该值。
net.ipv4.tcp_ecn = 0
#把TCP的直接拥塞通告(tcp_ecn)关掉
net.ipv4.tcp_sack = 1
#关闭tcp_sack
#启用有选择的应答(Selective Acknowledgment),
#这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);
#(对于广域网通信来说)这个选项应该启用,但是这会增加对 CPU 的占用。
net.ipv4.tcp_max_tw_buckets = 10000
#表示系统同时保持TIME_WAIT套接字的最大数量
net.ipv4.tcp_max_syn_backlog = 8192
#表示SYN队列长度,默认1024,改成8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_syncookies = 1
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_timestamps = 1
#开启TCP时间戳
#以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。
net.ipv4.tcp_tw_reuse = 1
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout = 10
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间。
net.ipv4.tcp_keepalive_time = 1800
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为30分钟。
net.ipv4.tcp_keepalive_probes = 3
#如果对方不予应答,探测包的发送次数
net.ipv4.tcp_keepalive_intvl = 15
#keepalive探测包的发送间隔
net.ipv4.tcp_mem
#确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。
#第一个值是内存使用的下限。
#第二个值是内存压力模式开始对缓冲区使用应用压力的上限。
#第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)。
net.ipv4.tcp_rmem
#与 tcp_wmem 类似,不过它表示的是为自动调优所使用的接收缓冲区的值。
net.ipv4.tcp_wmem = 30000000 30000000 30000000
#为自动调优定义每个 socket 使用的内存。
#第一个值是为 socket 的发送缓冲区分配的最少字节数。
#第二个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。
#第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)。
net.ipv4.ip_local_port_range = 1024 65000
#表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
net.ipv4.netfilter.ip_conntrack_max=204800
#设置系统对最大跟踪的TCP连接数的限制
net.ipv4.tcp_slow_start_after_idle = 0
#关闭tcp的连接传输的慢启动,即先休止一段时间,再初始化拥塞窗口。
net.ipv4.route.gc_timeout = 100
#路由缓存刷新频率,当一个路由失败后多长时间跳到另一个路由,默认是300。
net.ipv4.tcp_syn_retries = 1
#在内核放弃建立连接之前发送SYN包的数量。
net.ipv4.icmp_echo_ignore_broadcasts = 1
# 避免放大攻击
net.ipv4.icmp_ignore_bogus_error_responses = 1
# 开启恶意icmp错误消息保护
net.inet.udp.checksum=1
#防止不正确的udp包的攻击
net.ipv4.conf.default.accept_source_route = 0
#是否接受含有源路由信息的ip包。参数值为布尔值,1表示接受,0表示不接受。
#在充当网关的linux主机上缺省值为1,在一般的linux主机上缺省值为0。
#从安全性角度出发,建议你关闭该功能。