寻觅生命中的那一片浅草......

每月存档 九月, 2016

TCP的全连接队列和半连接队列

TCP连接的处理能力

最近某平台出现了白屏,黑屏的问题,查了下tcp方面的东西,目前来说,这些知识并没有解决问题,但总结下来,学习下。一个tcp连接进来,是否能连,是否能够被处理,是由系统和具体的应用程序来决定的。

系统层面

主要由2个内核参数,也就是2个队列决定,这是全局的,这里先解释下backlog这个单词的意思,就是堆积的作业,这里就是堆积的连接。

半连接

net.ipv4.tcp_max_syn_backlog 这个是半连接的队列长度,所谓半连接就是还没有完成3次握手的,状态是SYN_RECV

全连接

net.core.somaxconn 这个是一个socket的全连接队列长度,就是完成了3次握手但还没交给应用程序的队列长度,默认是128,状态是ESTABLISHED,需要注意,这个值,是基于socket的,例如socket1的队列不会影响到socket2的队列长度

在繁忙的系统,128这个值明显是不够的,如何判断somaxconn是否足够呢?可以用下面的命令,如果数字在不断增长,则应该增大somaxconn

netstat -s |grep listen

上面写的状态,可以用下面的命令看到

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

应用层序层面

在应用程序层面,则是在开启socket监听的时候,用listen(backlog)来设置,下面是我们的演示程序tcp_listen.py,88就是指可以有88个连接等待

import time
import socket
import struct

HOST = 'localhost'
PORT = 8000

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(88)

# /usr/include/linux/tcp.h
struct_tcp_info = struct.Struct('7B 24I')

while True:
    buf = s.getsockopt(socket.IPPROTO_TCP,
                       socket.TCP_INFO,
                       1024)
    tcp_info = struct_tcp_info.unpack(buf)
    tcpi_unacked, tcpi_sacked = tcp_info[11:13]
         
    print 'tcpi_unacked:', tcpi_unacked,\
        'tcpi_sacked:', tcpi_sacked

    time.sleep(1)

全连接中系统设置和应用设置的优先级

以下均是论述全连接backlog意思是堆积的作业数量

系统设置了tcp全连接的backlog,应用程序的listen又设置了backlog,那到底用那个?系统会对比2个backlog,取较小的一个,当系统设置net.core.somaxconn是128,而应用设置是listen(511),min(128,511),则是128

验证优先级

我们用tcp_listen.py来测试

CentOS 5.5

在CentOS 5.5上,很奇怪,执行后,print出来的一直都是0

tcpi_unacked: 0 tcpi_sacked: 0

如果要在CentOS5.5上测试,需要借助Systemtap来做检测,而不是看上面脚本print出来的值,安装Systemtap的过程略过(其实安装过程比较复杂)

probe begin {
 
  printf("inet listen Monitoring Started...\n")
 
}
 
  
 
probe kernel.function("inet_listen").return
 
{
 
    accept_backlog = $backlog
 
    recvq_backlog = @cast($sock->sk, "inet_connection_sock")->icsk_accept_queue->
 
            listen_opt->nr_table_entries
 
    printf("%-20s:accept backlog is %d, syn backlog is %d.\n", execname(),accept_backlog, recvq_backlog)
 
}

设置系统的backlog为1

sysctl -w net.core.somaxconn=1

程序设置,上面tcp_listen.py的值是88

s.listen(88)

我们开2个窗口,运行程序

stap listen.stp
python tcp_listen.py

可以看到 listen.stp的输出

python     :accept backlog is 1, syn backlog is 512.

看到了,程序是88,系统是1,最后取较小值,就是1,后面的512是半连接队列的长度

openSUSE 12.3 64bit ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在这个系统上tcplisten.py打印就正常,同样按照上面CentOS,设置系统的backlog的值为1,tcplisten.py的listen不变,执行tcp_listen.py,输出是::

tcpi_unacked: 0 tcpi_sacked: 1

上面的输出,0可以理解为半连接的队列长度,后面的1可以认为是全连接的队列长度

TCP状态转换

TCP状态转换图

tcp_status

上图是TCP的状态转换,我们来验证下tcp的连接处理过程

连接处理过程

当 tcp 建立连接的 3 次握手完成后,将连接置入 ESTABLISHED 状态并交付给应用程序的 backlog 队列时,会检查 backlog 队列是否已满。若已满,通常行为是将连接还原至 SYNRECV 状态,也就是把它放到半连接队列里,保持上文提到的tcplisten.py里的listen为88,net.core.somaxconn = 1,运行tcp_listen.py,再开2个tab,telnet 8000端口,就是有2个telnet连接到8000端口::

telnet localhost 8000

此时通过

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

看到的都是ESTABLISHED状态的连接,此时,再开一个tab,同样执行telnet,并执行上面的netstat命令,则会看到

SYN_RECV 1

由此,验证了上面的连接处理过程中的半连接和全连接过程。

我们的全连接队列长度是1,但为什么上面一共是telnet了3次,才会出现SYN_RECV呢?我个人理解是第一个直接交给程序处理了,第二个是放在全连接队列,第三个才是放到半连接队列

关于net.ipv4.tcp_abort_on_overflow

关于白屏问题,我以为是上面的值引起的,上面的值,默认是0的,看了下系统的配置,也是0,而且白屏的时候,还没去到游戏连接哪里呢,估计还在某平台那里。

那么这个值是做什么的呢?上面的实验可以看到,我们全连接的队列满了,还可以进入半连接队列,如果将net.ipv4.tcp_abort_on_overflow设置为1,如果全连接队列满了,则服务器直接发送RST给客户端,用tcpdump可以看到,客户端如果要连接,则需要重头发起连接。

命令

sysctl -w net.ipv4.tcp_abort_on_overflow=1

开3个tab进行telnet,可以看到进行到底三个的时候,就提示

telnet: connect to address 127.0.0.1: Connection reset by peer
telnet: Unable to connect to remote host: Connection reset by peer

同时,用netstat命令看,也不会有SYN_RECV状态的连接

结论

根据观察,net.core.somaxconn的值设置为1024比较合适,像静态资源机,如果没有使用cdn,则需要设置为2048,如果是跨服机,最好也设置为2048

20141226补充

最后解决黑屏和白屏、延迟5000ms的问题,是以下这个参数

echo 1 > /proc/sys/net/ipv4/tcp_sack

当时还设置了另外一个参数::

echo 1 > /proc/sys/net/ipv4/tcp_window_scaling

但查资料,这个参数在内核2.6.8开始,默认就是1了,所以这个对黑屏、白屏是没有影响的

最后,设置sack,虽然减轻了网络传输的压力,但对客户端造成了一定的性能损耗,见tcp_sack

zz:TCP和Nginx性能调优

绝对干货,沿用文中所说,当别人贴出优化配置的时候,并没有告诉你为什么要这么优化,也没有贴出具体的场景,而这2篇,除了告诉你优化了什么,还说明了为什么要这么做:

Part 1: Lessons learned tuning TCP and Nginx in EC2

Part 2: Lessons learned tuning TCP and Nginx in EC2

 

2016年九月
« 8月   10月 »
 1234
567891011
12131415161718
19202122232425
2627282930