随着企业网站访问量愈来愈大,服务器的压力也逐渐增长,主要体如今CPU使用率、内存、硬盘、网卡流量等方面资源占用状况很高。此时需对服务器性能进行调优,尽可能在保持服务器的现有数量,而后对其各个环节参数进行优化。node
本章向读者介绍Linux企业级性能服务器优化、TCP/IP报文、TCP三次握手及四次断开、Linux内核深刻优化、Linux内核故障解决方案及对Linux性能进行评估等。linux
TCP/IP 定义了电子设备如何连入因特网,以及数据如何在它们之间传输的标准。协议采用了4层的层级结构,每一层都呼叫它的下一层所提供的协议来完成本身的需求。安全
TCP负责发现传输的问题,一有问题就发出信号,要求从新传输,直到全部数据安全正确地传输到目的地,而IP是给因特网的每台联网设备规定一个地址。TCP/IP 协议数据封装的过程包括:用户数据通过应用层协议封装后传递给传输层,传输层封装TCP头部,交给网络层,网络层封装IP头部后,再交给数据链路层,数据链路层封装Ethernet帧头和帧尾,交给物理层,物理层以比特流的形式将数据发送到物理线路上。服务器
通常而言,不一样的协议层对数据包有不一样的称谓,数据包在传输层叫作段(segment),在网络层叫作数据报(datagram),在链路层叫作帧(frame)。数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理,如图所示:cookie
优化Linux服务器,须要了解TCP协议相关信息,例如TCP/IP数据报文的内容及如何传输的,如图所示为IP数据包报文详细结构图:网络
IP数据包详解以下:数据结构
q Source Port和Destination Port:分别占用16位,表示源端口号和目的端口号;用于区别主机中的不一样进程,而IP地址是用来区分不一样的主机的,源端口号和目的端口号配合上IP首部中的源IP地址和目的IP地址就能惟一的肯定一个TCP链接;并发 q Sequence Number:用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节在数据流中的序号;主要用来解决网络报乱序的问题;socket q Acknowledgment Number:32位确认序列号包含发送确认的一端所指望收到的下一个序号,所以,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志(下面介绍)为1时该确认序列号的字段才有效。主要用来解决不丢包的问题;tcp q Offset:给出首部中32 bit字的数目,须要这个值是由于任选字段的长度是可变的。这个字段占4bit(最多能表示15个32bit的的字,即4*15=60个字节的首部长度),所以TCP最多有60字节的首部。然而,没有任选字段,正常的长度是20字节; q TCP Flags:TCP首部中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次为URG,ACK,PSH,RST,SYN,FIN。每一个标志位的意思以下: ① URG:此标志表示TCP包的紧急指针域(后面立刻就要说到)有效,用来保证TCP链接不被中断,而且督促中间层设备要尽快处理这些数据; ② ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0; ③ PSH:这个标志位表示Push操做。所谓Push操做就是指在数据包到达接收端之后,当即传送给应用程序,而不是在缓冲区中排队; ④ RST:这个标志表示链接复位请求。用来复位那些产生错误的链接,也被用来拒绝错误和非法的数据包; ⑤ SYN:表示同步序号,用来创建链接。SYN标志位和ACK标志位搭配使用,当链接请求的时候,SYN=1,ACK=0;链接被响应的时候,SYN=1,ACK=1;这个标志的数据包常常被用来进行端口扫描。扫描者发送一个只有SYN的数据包,若是对方主机响应了一个数据包回来,就代表这台主机存在这个端口;可是因为这种扫描方式只是进行TCP三次握手的第一次握手,所以这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求一个链接严格的进行TCP的三次握手; ⑥ FIN:表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据能够传送了,发送FIN标志位的TCP数据包后,链接将被断开。这个标志的数据包也常常被用于进行端口扫描。 q Window:窗口大小,也就是有名的滑动窗口,用来进行流量控制; |
TCP是面向链接的,不管哪一方向另外一方发送数据以前,都必须先在双方之间创建一条链接。在TCP/IP协议中,TCP协议提供可靠的链接服务,链接是经过三次握手进行初始化的。三次握手的目的是同步链接双方的序列号和确认号并交换TCP窗口大小信息。如图所示:
(1)TCP三次握手原理:
第一次握手:创建链接。客户端发送链接请求报文段,将SYN位置为1,Sequence Number为x;而后客户端进入SYN_SENT状态,等待服务器的确认; 第二次握手:服务器收到SYN报文段。服务器收到客户端的SYN报文段,须要对这个SYN报文段进行确认,设置Acknowledgment Number为x+1(Sequence Number+1);同时,本身本身还要发送SYN请求信息,将SYN位置为1,Sequence Number为y;服务器端将上述全部信息放到一个报文段(即SYN+ACK报文段)中,一并发送给客户端,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK报文段。而后将Acknowledgment Number设置为y+1,向服务器发送ACK报文段,这个报文段发送完毕之后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
如图所示为基于tcpdump抓取TCP/IP三次握手及数据包传输过程:
(2)TCP四次挥手原理
q 第一次挥手:主机A(可使客户端,能够是服务器端),设置Sequence Number和Acknowledgment Number,向主机B发送一个FIN报文段;此时,主机A进入FIN_WAIT_1状态;这表示主机A没有数据要发送给主机B; q 第二次挥手:主机B收到了主机A发送的FIN报文段,向主机A回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机A进入FIN_WAIT_2状态;主机B告诉主机A,我“赞成”你的关闭请求; q 第三次挥手:主机B向主机A发送FIN报文段,请求关闭链接,同时主机B进入LAST_ACK状态; q 第四次挥手:主机A收到主机B发送的FIN报文段,向主机B发送ACK报文段,而后主机A进入TIME_WAIT状态;主机B收到主机A的ACK报文段之后,就关闭链接;此时,主机A等待2MSL后依然没有收到回复,则证实Server端已正常关闭,那好,主机A也能够关闭链接。 |
如图所示为基于tcpdump抓取TCP/IP四次挥手及数据包传输过程 :
为了防止失控的进程破坏系统的性能,Unix和Linux会跟踪进程使用的大部分资源,并容许用户和系统管理员使用对进程的资源限制,例如控制某个进程打开的系统文件数、对某个用户打开系统进程数进行限制等,通常限制手段包括:软限制和硬限制。
软限制(soft limit)是内核实际执行的限制,任何进程均可以将软限制设置为任意小于等于对进程限制的硬限制的值,(noproc)最大线程数和(nofile)文件数;
硬限制(hard limit)是能够在任什么时候候任何进程中设置,但硬限制只能由超级用户修改。
Linux系统一切皆文件,对Linux进行各类操做,实际上是对文件进行操做,文件可分为:普通文件、目录文件、连接文件和设备文件。而文件描述符(file descriptor)是内核为了高效管理已被打开的文件所建立的索引,其值一个非负整数(一般是小整数),用于指代被打开的文件,全部执行I/O操做的系统调用都经过文件描述符。
Linux系统默认已经打开的文件描述符包括:STDIN_FILENO 0表示标准输入、STDOUT_FILENO 1表示标准输出、STDERR_FILENO 2表示标准错误输出,默认打开一个新文件,它的文件描述符为3。
每一个文件描述符与一个打开文件相对应,不一样的文件描述符能够指向同一个文件。相同的文件能够被不一样的进程打开,也能够在同一个进程中被屡次打开。
Linux系统为每一个进程维护了一个文件描述符表,该表的值都从0开始的,在不一样的进程中你会看到相同的文件描述符,相同文件描述符有可能指向同一个文件,也有可能指向不一样的文件。Linux内核对文件操做,维护了3个数据结构概念以下:
进程级的文件描述符表;
系统级的打开文件描述符表;
文件系统的i-node表;
其中进程级的描述符表的每个条目记录了单个文件描述符的相关信息,例如控制文件描述符操做的一组标志及对打开文件句柄的引用。Linux内核对全部打开的文件都维护了一个系统级的描述符表(open file description table)。将描述符表中的记录行称为打开文件句柄(open file handle),一个打开文件句柄存储了与一个打开文件相关的所有信息,详细信息以下:
当前文件偏移量;
打开文件时所使用的状态标识;
文件访问模式;
与信号驱动相关的设置;
对该文件i-node对象的引用;
文件类型和访问权限;
指针,指向该文件所持有的锁列表;
文件的各类属性。
默认Linux内核对每一个用户设置了打开文件最大数为1024,对于高并发网站,是远远不够的,须要将默认值调整到更大,调整方法有两种:
Linux每一个用户打开文件最大数临时设置方法,重启服务器该参数无效,命令行终端执行以下命令:
ulimit -n 65535
Linux每一个用户打开文件最大数永久设置方法,将以下代码加入内核限制文件/etc/security/limits.conf的末尾:
* soft noproc 65535 * hard noproc 65535 * soft nofile 65535 * hard nofile 65535
如上设置为对每一个用户分别设置nofile、noproc最大数,若是须要对Linux整个系统设置文件最大数限制,须要修改/proc/sys/fs/file-max中的值,该值为Linux总文件打开数,例如设置为:echo 3865161233 >/proc/sys/fs/file-max。
Linux /proc/sys目录下存放着多数内核的参数,而且能够在系统运行时进行更改,通常从新启动机器就会失效。而/etc/sysctl.conf是一个容许改变正在运行中的Linux系统的接口,它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,修改内核参数永久生效。
/proc/sys下内核文件与配置文件sysctl.conf中变量存在着对应关系,即修改sysct.conf配置文件,实际上是修改/proc/sys相关参数,因此对Linux内核优化只需修改/etc/sysctl.conf文件便可。以下为BAT企业生产环境/etc/sysct.conf内核完整参数:
net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 kernel.sysrq = 0 kernel.core_uses_pid = 1 net.ipv4.tcp_syncookies = 1 kernel.msgmnb = 65536 kernel.msgmax = 65536 kernel.shmmax = 68719476736 kernel.shmall = 4294967296 net.ipv4.tcp_max_tw_buckets = 10000 net.ipv4.tcp_sack = 1 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_rmem = 4096 87380 4194304 net.ipv4.tcp_wmem = 4096 16384 4194304 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.core.netdev_max_backlog = 262144 net.core.somaxconn = 262144 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_max_syn_backlog = 262144 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_synack_retries = 1 net.ipv4.tcp_syn_retries = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_fin_timeout = 1 net.ipv4.tcp_keepalive_time = 30 net.ipv4.ip_local_port_range = 1024 65535
Linux内核常见参数详解:
net.ipv4.tcp_timestamps = 1 该参数控制RFC 1323 时间戳与窗口缩放选项; net.ipv4.tcp_sack = 1 选择性应答(SACK)是 TCP 的一项可选特性,能够提升某些网络中全部可用带宽的使用效率; net.ipv4.tcp_fack = 1 打开FACK(Forward ACK) 拥塞避免和快速重传功能; net.ipv4.tcp_retrans_collapse = 1 打开重传重组包功能,为0的时候关闭重传重组包功能; net.ipv4.tcp_syn_retries = 5 对于一个新建链接,内核要发送多少个SYN 链接请求才决定放弃; net.ipv4.tcp_synack_retries = 5 tcp_synack_retries显示或设定Linux在回应SYN要求时尝试多少次从新发送初始SYN,ACK封包后才决定放弃; net.ipv4.tcp_max_orphans = 131072 系统所能处理不属于任何进程的TCP sockets最大数量; net.ipv4.tcp_max_tw_buckets = 5000 系统同时保持TIME_WAIT套接字的最大数量,若是超过这个数字,TIME_WAIT套接字将马上被清除并打印警告信息; 默认为180000,设为较小数值此项参数能够控制TIME_WAIT套接字的最大数量,避免服务器被大量的TIME_WAIT套接字拖死; net.ipv4.tcp_keepalive_time = 30 net.ipv4.tcp_keepalive_probes = 3 net.ipv4.tcp_keepalive_intvl = 3 若是某个TCP链接在空闲30秒后,内核才发起probe(探查); 若是probe 3次(每次3秒既tcp_keepalive_intvl值)不成功,内核才完全放弃,认为该链接已失效; net.ipv4.tcp_retries1 = 3 放弃回应一个TCP 链接请求前﹐须要进行多少次重试; net.ipv4.tcp_retries2 = 15 在丢弃激活(已创建通信情况)的TCP链接以前﹐须要进行多少次重试; net.ipv4.tcp_fin_timeout = 30 表示若是套接字由本端要求关闭,这个参数决定了它保持在 FIN-WAIT-2状态的时间; net.ipv4.tcp_tw_recycle = 1 表示开启TCP链接中TIME-WAIT sockets的快速回收,默认为0,表示关闭; net.ipv4.tcp_max_syn_backlog = 8192 表示SYN队列的长度,默认为1024,加大队列长度为8192,能够容纳更多等待链接的网络链接数; net.ipv4.tcp_syncookies = 1 TCP创建链接的 3 次握手过程当中,当服务端收到最初的 SYN 请求时,会检查应用程序的syn_backlog队列是否已满,启用syncookie,能够解决超高并发时的Can’t Connect` 问题。可是会致使 TIME_WAIT 状态fallback为保持2MSL时间,高峰期时会致使客户端无可复用链接而没法链接服务器; net.ipv4.tcp_orphan_retries = 0 关闭TCP链接以前重试多少次; net.ipv4.tcp_mem = 178368 237824 356736 net.ipv4.tcp_mem[0]: 低于此值,TCP没有内存压力; net.ipv4.tcp_mem[1]: 在此值下,进入内存压力阶段; net.ipv4.tcp_mem[2]: 高于此值,TCP拒绝分配socket; net.ipv4.tcp_tw_reuse = 1 表示开启重用,容许将TIME-WAIT sockets从新用于新的TCP链接; net.ipv4.ip_local_port_range = 1024 65000 表示用于向外链接的端口范围; net.ipv4.ip_conntrack_max = 655360 在内核内存中netfilter能够同时处理的“任务”(链接跟踪条目); net.ipv4.icmp_ignore_bogus_error_responses = 1 开启恶意icmp错误消息保护; net.ipv4.tcp_syncookies = 1 开启SYN洪水攻击保护。
企业生产环境Linux服务器正常运行,因为某种缘由会致使内核报错或者抛出不少信息,根据系统SA能够快速定位Linux服务器故障,Linux内核日志通常存在messages日志中,能够经过命令tail -fn 100 /var/log/messages查看Linux内核日志,以下为Linux内核常见报错日志及生产环境解决报错的方案。
一、Linux内核抛出net.ipv4.tcp_max_tw_buckets错误
Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow |
根据TCP协议定义的3次握手及四次断开链接规定,发起socket主动关闭的一方Socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime)。
若是该值设置太小致使,当系统Time wait数量超过默认设置的值,即会抛出如上的警告信息,须要增长net.ipv4.tcp_max_tw_buckets的值,警告信息消除。
固然也不能设置过大,对于一个处理大量短链接的服务器,若是是由服务器主动关闭客户端的链接,将致使服务器端存在大量的处于TIME_WAIT状态的Socket,甚至比处于Established状态下的Socket多的多,严重影响服务器的处理能力,甚至耗尽可用的Socket而中止服务,TIME_WAIT是TCP协议用以保证被从新分配的Socket不会受到以前残留的延迟重发报文影响的机制,是TCP传输必要的逻辑保证。
二、Linux内核抛出Too many open files错误:
Benchmarking localhost (be patient) socket: Too many open files (24) socket: Too many open files (24) socket: Too many open files (24) socket: Too many open files (24) socket: Too many open files (24) |
每一个文件描述符与一个打开文件相对应,不一样的文件描述符能够指向同一个文件。相同的文件能够被不一样的进程打开,也能够在同一个进程中被屡次打开。Linux内核对应每一个用户打开的文件最大数通常为1024,须要将该值调高知足大并发网站的访问。
Linux每一个用户打开文件最大数永久设置方法,将以下代码加入内核限制文件/etc/security/limits.conf的末尾,Exit退出终端,从新登陆即生效:
* soft noproc 65535 * hard noproc 65535 * soft nofile 65535 * hard nofile 65535
三、Linux内核抛出possible SYN flooding on port 80. Sending cookies错误:
May 31 14:20:14 localhost kernel: possible SYN flooding on port 80. Sending cookies. May 31 14:21:28 localhost kernel: possible SYN flooding on port 80. Sending cookies. May 31 14:22:44 localhost kernel: possible SYN flooding on port 80. Sending cookies. May 31 14:25:33 localhost kernel: possible SYN flooding on port 80. Sending cookies. May 31 14:27:06 localhost kernel: possible SYN flooding on port 80. Sending cookies. May 31 14:28:44 localhost kernel: possible SYN flooding on port 80. Sending cookies. May 31 14:28:51 localhost kernel: possible SYN flooding on port 80. Sending cookies. May 31 14:31:01 localhost kernel: possible SYN flooding on port 80. Sending cookies. |
此问题是因为SYN 队列已满,而触发SYN cookies,通常是因为大量的访问,或者恶意访问致使,也称之为SYN Flooding洪水攻击,与DDOS攻击相似。
完整的TCP链接的三次握手,假设一个用户A向服务器发送了SYN报文后忽然死机或掉线,那么服务器在发出SYN+ACK应答报文后是没法收到客户端的ACK报文的(第三次握手没法完成),这种状况下服务器端通常会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的链接,这段时间的长度咱们称为SYN Timeout,通常来讲这个时间是分钟的数量级(大约为30秒-2分钟)。
一个用户出现异常致使服务器的一个线程等待1分钟并非什么很大的问题,但若是有一个恶意的攻击者大量模拟这种状况,服务器端将为了维护一个很是大的半链接列表而消耗很是多的资源,数以万计的半链接,即便是简单的保存并遍历也会消耗很是多的CPU时间和内存,况且还要不断对这个列表中的IP进行SYN+ACK的重试。
实际上若是服务器的TCP/IP栈不够强大,最后的结果每每是堆栈溢出崩溃,即便服务器端的系统足够强大,服务器端也将忙于处理攻击者伪造的TCP链接请求而无暇理睬客户的正常请求(毕竟客户端的正常请求比率很是之小),此时从正常客户的角度看来,服务器失去响应,服务器拒绝提供服务,服务器受到了DDOS攻击,这里攻击的手段为DDOS中SYN Flood攻击(SYN洪水攻击)。
防御DDOS攻击有两种手段,一是基于硬件专业防火墙、二是基于Linux内核简单防御,若是攻击流量特别大,单纯配置内核参数是没法抵挡的,还得依靠专业级硬件防火墙,以下为Linux内核防御DDOS优化参数,加入以下代码便可:
net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.ip_local_port_range = 1024 65000 net.ipv4.tcp_max_syn_backlog = 8192 net.ipv4.tcp_max_tw_buckets = 8000 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2
四、Linux内核抛出ip_conntrack: table full, dropping packet.错误:
May 6 11:15:07 localhost kernel: nf_conntrack:table full, dropping packet. May 6 11:19:13 localhost kernel: nf_conntrack:table full, dropping packet. May 6 11:20:34 localhost kernel: nf_conntrack:table full, dropping packet. May 6 11:23:12 localhost kernel: nf_conntrack:table full, dropping packet. May 6 11:24:07 localhost kernel: nf_conntrack:table full, dropping packet. May 6 11:24:13 localhost kernel: nf_conntrack:table full, dropping packet. May 6 11:25:11 localhost kernel: nf_conntrack:table full, dropping packet. May 6 11:26:25 localhost kernel: nf_conntrack:table full, dropping packet. |
因为该服务器开启了iptables防火墙,WEB服务器收到了大量的链接,iptables会把全部的链接都作连接跟踪处理,这样iptables就会有一个连接跟踪表,当这个表满的时候,就会出现上面的错误。ip_conntrack是linux NAT的一个跟踪链接条目的模块,ip_conntrack模块会使用一个哈希表记录 tcp 通信协议的established connection记录。
若是是CentOS6.x系统,需执行:modprobe nf_conntrack命令,而后在内核优化文件中加入以下代码,sysctl –p使其内核文件生效,便可解决该报错:
net.nf_conntrack_max = 655360 net.netfilter.nf_conntrack_tcp_timeout_established = 36000
若是是CentOS5.x系统,需执行:modprobe ip_conntrack命令,而后在内核优化文件中加入以下代码,sysctl –p使其内核文件生效,便可解决该报错:
net.ipv4.ip_conntrack_max = 655350 net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 10800
Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow Sep 23 04:45:55 localhost kernel: TCP: time wait bucket table overflow |