[tcp] WEB服务,Linux下的内核参数调优

前言:
web类应用通常会部署像nginx、tomcat、php等应用程序,使用默认的内核参数设置知足大部分场景,若是优化内核参数,也能够释放很多服务器性能,尤为是在高并发下php

一.SYN状态的内核参数调优

大量SYN_SENT
这种是主动链接服务端,而未获得响应,也就是SYN超时,通常是服务端根本不存在或者没法访问
如,我随便telnet一个位置的IP和端口前端

telnet 172.18.11.110:90
[root@test bbs]# ss -an|grep SYN
SYN-SENT   0      1            172.16.196.145:55052        172.18.11.110:90

除了以上,还有种就是你的服务出现异常,好比mysql服务器宕机了,web服务去访问mysql数据库的时候就连不上,也会出现SYN_SENT状态,但不管哪一种,都是主动发起链接致使的,所以业务上解决更好mysql

net.ipv4.tcp_syn_retries = 2
新建链接若是无响应,内核要发送多少次SYN链接才放弃,默认值为5nginx

在Linux下,默认重试次数为5次,该值不能大于255,重试的间隔时间从1s开始每次都翻倍(由于隔一秒重试后还会等待响应,所以其实是从3秒开始),5次的重试时间间隔为3s, 7s, 15s, 31s, 63s,总共63s,TCP才会把断开这个链接。统计成公式2^(n+1) - 1,所以设置越大,翻倍越多,对应内网环境,这个值修改成2比较合适web

大量SYN_RECV
大量的SYN出现有两种状况,多是攻击,也多是正常的业务请求,不管哪一种,都大量的占用了服务器资源redis

net.ipv4.tcp_synack_retries = 2
跟参数net.ipv4.tcp_syn_retries同样,只是这个内核参数是控制回应SYN失败的重试次数,默认值也是5,和上面同样修改成2sql

其余内核参数调整
net.ipv4.tcp_syncookies = 1
开启SYN cookies,当出现SYN等待队列溢出时,启动cookies来处理数据库

什么是SYN cookies?咱们知道SYN攻击是一系列伪造IP源地址的SYN包,IP地址是随意选择且不提供攻击者任何的线索,SYN攻击持续直到服务的SYN队列被用满。若是启用该参数,此时SYN cookies会将TCP请求的SYN缓存起来,当服务器正常的时候,再处理,可是若是攻击并发很高很大,其实用处不大,所以只能少许防范编程

SYN cookies可参考:https://blog.csdn.net/chenmo1...vim

net.ipv4.tcp_max_syn_backlog = 65535
指定所能接受SYN同步包的最大客户端数量,即半链接上限,默认值为128,对于web服务,频繁大量的SYN同步包,应该放大这个值

注:这个值应该>=net.core.somaxconn,net.core.somaxconn后面会提到

二.FIN_WAIT_2状态的内核参数调优

FIN_WAIT_2是主动关闭端等待对端关闭链接的状态,若是被动关闭不发送FIN关闭链接,那么这个状态就会一直存在,固然Linux有针对该状态的超时时间,默认为60秒

net.ipv4.tcp_fin_timeout = 10

三.TIME_WAIT状态的内核参数调优

TIME_WAIT是主动关闭端的状态,也称为2MSL等待状态,也就是2倍的MSL时间。在RFC 793[Postel 1981c]指出MSL为2分钟,然而现实中的经常使用值是30秒,1分钟或者2分钟(Linux设置为30秒),Linux也没有提供可以修改TIME_WAIT状态时间的接口,除非从新编译系统内核

MSL的理解
MSL是英文Maximum Segment Lifetime的缩写,翻译为"最长报文段寿命",每一个具体TCP实现必须选择一个报文段最大生存时间(Maximum Segment Lifetime),而这个最大生存时间是任何报文段被丢弃前在网络内的最长时间

MSL的时间是有限的,由于TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL(time to live)字段,TTL可译为生存时间,IP数据报每通过一个路由器,它的值就减1,当这个值为0时,数据报则被丢弃

为何等待2MSL
1.确保有足够的时间让服务端收到ACK,如没有收到,则会响应对方新的FIN+ACK封包。好比主动关闭端(客户端)发送了最后一个ACK报文段给被动关闭端(服务端),但这个ACK报文段有可能丢失,若是服务端没有收到这个ACK,那么处于LAST_ACK的服务端在超时后回重发FIN+ACK报文段,这样客户端就能在2MSL时间内收到这个重发的FIN+ACK报文段。若是客户端发送了最后的ACK报文不进入TIME_WAIT而是当即释放链接,那么就没法收到客户端重发的FIN+ACK报文段。所以等待2MSL是为了更安全的断开链接

2.有足够的时间让处于TIME_WAIT状态的链接不会跟后面的链接混在一块儿。好比一些延迟的包发过来,可是若是没有TIME_WAIT,那么就发到了新链接上,这样就混为一团,而若是是TIME_WAIT,则会丢弃这些延迟的包

等待2MSL的缺点
TCP链接在2MSL等待期间,这个处于TIME_WAIT状态的链接(客户端的IP地址和端口编号,服务器的IP地址和端口号)不能再被使用,它只能在2MSL结束后才能再被使用,而这些TIME_WAIT状态占用大量服务资源,对于web服务来讲是不合理的

修改内核参数防止由于2MSL致使TIME_WAIT过多
对于web服务器,因为咱们须要常常去链接mysql、redis或者一些RPC调用等,会有大量的主动关闭状态(TIME_WAIT),所以能够修改内核参数限制TIME_WAIT的数量

net.ipv4.tcp_max_tw_buckets = 20000
限制timewait 的数量,防止大量timewait致使系统负载升高,一旦达到限定值,则强制清理TIME_WAIT状态的链接并在打印系统日志(time wait bucket table overflow),该参数官方文档说明主要用来对抗DDos攻击

net.ipv4.tcp_tw_recycle= 1
启用timewait快速回收

net.ipv4.tcp_timestamps = 0
时间戳,0关闭,1开启。不能和net.ipv4.tcp_tw_recycle参数同时开启,由于一旦开启net.ipv4.tcp_tw_recycle,服务器就会检查包的时间戳,若是对方发来的包的时间戳是乱跳或者说时间戳是滞后的,这样服务器就不会回复,服务器会把带了"倒退"的时间戳包看成是"recycle"的tw链接的重传数据,不是新的请求,因而丢掉不回包,就容易出现syn不响应

net.ipv4.tcp_tw_reuse = 1
开启重用,容许将TIME-WAIT sockets 从新用于新的TCP 链接

TIME_WAIT总结
其实TIME_WAIT是主动断开链接,因此若是让对方主动断开链接的话,那么这个TIME_WAIT问题就对方的了。因此若是这个问题出现过多,多从业务着手,好比HTTP服务,NGINX设置keepalive参数(浏览器会重用一个TCP链接来处理多个HTTP请求),而后让客户端断开链接,固然这个要设置好keepalive_timeout的超时时间,由于有些浏览器可能不会主动断开链接

而若是是主动链接mysql、redis等后端调用,能够考虑使用长链接来避免TIME_WAIT过多的问题

四.长链接(keepalive)的内核参数调整

Linux下,keepalive不是默认开启,也无内核参数控制,它须要在TCP的socket中单独开启,Linux内核影响keepalive的参数目的仅仅是探测TCP链接是否存活,而后处理异常链接

net.ipv4.tcp_keepalive_time = 120 单位秒,表示TCP链接在多少秒没有数据报文传输时启动探测报文,探测链接是否正常
net.ipv4.tcp_keepalive_intvl = 5 单位秒,先后探测报文之间的时间间隔
net.ipv4.tcp_keepalive_probes = 3 探测次数,超过设置后丢弃

五.TCP/UDP内存参数调整

(1)TCP内存使用设置

针对TCP socket buffer
net.ipv4.tcp_mem = 94500000 915000000 927000000
指定TCP内存的总体使用情况,单位为页。这3个值为TCP总体内存【低、压力、高】,在web服务中,放大这个值便可
第一个值tcp_mem[0]:当TCP全局分配的页数低于此数时,TCP不调整其内存分配
第二个值tcp_mem[1]:当TCP分配的内存量超过这个页数,进入内存压力模式,TCP调节内存消耗
第三个值tcp_mem[2]:TCP全局使用的最大页数分配,这个会值覆盖任何其余限制,如超过,全部的新的TCP的buffer(缓冲区)内存分配都会失败

其实咱们能够设置这个值较大,只要不限制系统分配内存,而后以监控来应对内存问题,通常来讲,根据业务所选配置,很难将内存耗尽,不然优化的就不只仅是这个参数了

net.ipv4.tcp_rmem = 4096 87380 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304
上面两组参数表示单个TCP链接上的读写buffer(缓冲)内存上限,单位字节,这三个值分别为最小值、默认值(会覆盖rmem_default、wmem_default配置)、最大值

最小值:TCP socket的发送缓冲区(tcp_rmem)/接收缓冲区(tcp_wmem)的内存,默认1页(4K)

默认值:TCP socket使用的发送缓冲区(tcp_rmem)/接收缓冲区(tcp_wmem)初始大小,这个值会覆盖(net.core.wmem_default/net.core.rmem_default),通常设置要低于(net.core.wmem_default/net.core.rmem_default)这个值,默认值为16K

最大值:TCP socket使用的发送缓冲区(tcp_rmem)/接收缓冲区(tcp_wmem)的最大大小,这个值不会覆盖(net.core.wmem_max/net.core.rmem_max),默认为4M

这两个内核参数的设置主要是针对每个TCP链接来讲的,使用默认设置就差很少了,若是设置太大,单个TCP链接占用过多内存也是有问题的

什么是TCP读写buffer(缓冲)?
实际上,TCP链接所用内存的多少是由读写buffer大小决定,对读buffer来说,当收到对端链接的TCP报文时,会致使读buffer内存增长,若是这个报文加上当前读buffer内存超过tcp_rmem[3]上限,那么该报文将被丢弃。只有当调用read、recv这样的方法读取TCP流时,读buffer内存就会减小,所以读buffer内存是一个动态变化的,用多少就分配多少buffer,若是这个链接空闲时,而用户进程已经把链接上收到的数据都消费了,那么读buffer使用的内存就为0了

对于写buffer也是同样的,在socket编程中,当调用send或者write时,就会形成写buffer增大,那么何时减小?就是当接收到对端TCP链接发来的ACK确认了报文成功发送时,写buffer就会减小,相似于我给你发一个文件,我先拷贝出来发给你,我确认你收到了,我就把这个源文件删除,以避免占用空间,若是确认没收到,那么我会重发

因此读写buffer是一直不停变化的,那么怎样的场景会致使读写buffer达到上限呢?就读buffer而言,好比接收TCP对端报文,对端发了不少不少报文,我读取后没法及时读取(read和recv),致使读buffer堆积愈来愈多,最终达到上限,最后丢弃报文,写buffer也同样,send或者write大量的报文时,若是TCP对端不能及时read和recv就会致使写buffer堆积。

针对系统的读写buffer参数调整
net.core.rmem_default = 4194304 默认读buffer大小,单位字节
net.core.wmem_default = 4194304 默认写buffer大小,单位字节
net.core.rmem_max = 4194304 最大读buffer大小,单位字节
net.core.wmem_max = 4194304 最大写buffer大小,单位字节
看到其定义,是否是以为跟net.ipv4.tcp_mem、net.ipv4.tcp_rmem、net.ipv4.tcp_wmem含义很重合呢?

其实(net.ipv4.tcp_mem、net.ipv4.tcp_rmem、net.ipv4.tcp_wmem)这几个参数只控制TCP socket的内存大小,并且若是遇到TCP socket申请内存,(net.core.rmem_default、net.core.wmem_default)会被(net.ipv4.tcp_rmem、net.ipv4.tcp_wmem)覆盖

因此(net.core.rmem_default、net.core.wmem_default、net.core.rmem_max、net.core.wmem_max)控制系统全部协议的读写buffer大小

(2)UDP协议内存使用设置

net.ipv4.udp_mem = 752832 1003776 1505664
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096
这几个参数针对UDP协议,则跟上面TCP的含义一致

六.其余内核参数

net.ipv4.ip_local_port_range = 1024 65000
表示用于向外链接的临时端口范围。缺省状况下很小:32768到61000,由于主动链接须要用到不少临时端口(如链接mysql、redis),而临时端口最大值为(2^16-1)65535,1000以前通常为系统保留端口,因此建议设置为1024到65000的较大范围

net.core.somaxconn = 65535
net.core.somaxconn表示socket监听(listen)的backlog上限,backlog是socket的监听队列,也就是服务端所能accept(socket编程中accpet()函数为创建TCP链接接受链接状态)即处理数据的最大客户端数量队列,默认值为128,若是队列满了的时候新来一条创建链接,该链接会被拒绝

该值应当小于等于net.ipv4.tcp_max_syn_backlog,由于net.ipv4.tcp_max_syn_backlog参数控制的SYN队列客户端的数量,还在创建链接以前,所以设置为65535同样比较合适

fs.file-max = 6553600
设置系统全部进程一共能够打开多少个文件句柄,这是一个系统级的设置,管控的是全部进程总共能够同时打开多少文件句柄,若是多个进程打开了较多文件就会致使文件句柄不足,所以设置较大值,不过要注意程序打开的文件越多,就占用更多的内存,所以要根据业务和服务器配置起来设置

若是想单独对某个进程设置能够打开多少文件句柄,那么可使用ulimit -n命令设置,但该命令只对当前session生效,默认值为1024
ulimit -n 655350

也能够写入文件永久生效,对每一个进程的打开文件数量限制
vim /etc/security/limits.conf
* soft nofile 655350
* hard nofile 655350

总结

如今多数线上业务,服务器不多暴露在外网了,前端通常有负载均衡、防火墙等代理。甚至服务器已经变成VPC(虚拟内网)环境,将这些服务器隔离在外网环境以外,这样就减小了像DDOS等攻击,这些攻击通常都让外部代理承受了。

对于服务器的一些内核性能参数范围,若是网络环境及架构设计好,一些范围参数能够设置的偏大,性能偏极限一些,这样能最大释放服务器的性能,其余的就用系统默认的参数配置便可。对于WEB服务的优化,是多方面的,内核参数仅仅是释放了服务器本该有的性能,而更高的承载能力,须要从服务器配置、网络、架构、数据库及缓存和实际业务应用等多方面着手,不一样的调整知足不一样的需求

RT:以上有些地方可能会解释得不对,还望指正,一块儿学习

相关文章
相关标签/搜索