Socket TCP相关

2017-12-03  本文已影响237人  大富帅

TCP 连接3次握手,断开连接4次握手图示

TCP.png

tcp/ip协议listen函数中backlog参数的含义

backlog.png

1、client发送SYN到server,将状态修改为SYN_SEND,如果server收到请求,则将状态修改为SYN_RCVD,并把该请求放到syns queue队列中。
2、server回复SYN+ACK给client,如果client收到请求,则将状态修改为ESTABLISHED,并发送ACK给server。
3、server收到ACK,将状态修改为ESTABLISHED,并把该请求从syns queue中放到accept queue。

在linux系统内核中维护了两个队列:syns queue和accept queue

syns queue
用于保存半连接状态的请求,其大小通过/proc/sys/net/ipv4/tcp_max_syn_backlog指定,一般默认值是512,不过这个设置有效的前提是系统的syncookies功能被禁用。互联网常见的TCP SYN FLOOD恶意DOS攻击方式就是建立大量的半连接状态的请求,然后丢弃,导致syns queue不能保存其它正常的请求。

accept queue
用于保存全连接状态的请求,其大小通过/proc/sys/net/core/somaxconn指定,在使用listen函数时,内核会根据传入的backlog参数与系统参数somaxconn,取二者的较小值。

在Linux下,backlog指定的是complete queue的大小,而incomplete queue的大小可以由系统管理员在 /proc/sys/net/ipv4/tcp_max_syn_backlog下进行统一配置。

如果accpet queue队列满了,server将发送一个ECONNREFUSED错误信息Connection refused到client。


[为何PHP5.5.6中fpm backlog Changed default listen() backlog to 65535]

其中理由是“backlog值为65535太大了。会导致前面的nginx(或者其他客户端)超时”,而且提交者举例计算了一下,假设FPM的QPS为5000,那么65535个请求全部处理完需要13s的样子。但前端的nginx(或其他客户端)已经等待超时,关闭了这个连接。当FPM处理完之后,再往这个SOCKET ID 写数据时,却发现连接已关闭,得到的是“error: Broken Pipe”,在nginx、redis、apache里,默认的backlog值兜是511。故这里也建议改为511

backlog的定义是已连接但未进行accept处理的SOCKET队列大小,已是(并非syn的SOCKET队列)。如果这个队列满了,将会发送一个ECONNREFUSED错误信息给到客户端,即 linux 头文件 /usr/include/asm-generic/errno.h中定义的“Connection refused”,(如果协议不支持重传,该请求会被忽略)

在linux 2.2以前,backlog大小包括了半连接状态和全连接状态两种队列大小。linux 2.2以后,分离为两个backlog来分别限制半连接SYN_RCVD状态的未完成连接队列大小跟全连接ESTABLISHED状态的已完成连接队列大小。互联网上常见的TCP SYN FLOOD恶意DOS攻击方式就是用/proc/sys/net/ipv4/tcp_max_syn_backlog来控制的,可参见《TCP洪水攻击(SYN Flood)的诊断和处理》。

在使用listen函数时,内核会根据传入参数的backlog跟系统配置参数/proc/sys/net/core/somaxconn中,二者取最小值,作为“ESTABLISHED状态之后,完成TCP连接,等待服务程序ACCEPT”的队列大小。在kernel 2.4.25之前,是写死在代码常量SOMAXCONN,默认值是128。在kernel 2.4.25之后,在配置文件/proc/sys/net/core/somaxconn (即 /etc/sysctl.conf 之类 )中可以修改。我稍微整理了流程图,如下:


uwsgi 启动的时候listen 设置大于128是失败的,报错:

Listen queue size is greater than the system max net.core.somaxconn (128).

这个值是来自 :
cat /proc/sys/net/core/somaxconn
128

> ss -tl
State       Recv-Q Send-Q                       Local Address:Port                           Peer Address:Port 
LISTEN      0      128                                      *:8011                                      *:*       
LISTEN      0      128                              127.0.0.1:11211                                     *:*       
LISTEN      0      128                                      *:8012                                      *:*       
LISTEN      0      10                                       *:9005                                      *:*       
LISTEN      0      128                                      *:8013                                      *:*       
LISTEN      0      5                                        *:9006                                      *:*       
LISTEN      0      128                              127.0.0.1:2222                                      *:*   

有的进程不是监听端口IP的,是监听unix socket
所以用下面命令可以找到,进程监听的socket以及它的backlog是多少

ss -pl|grep owan
u_str  LISTEN     0      13     /tmp/owan_web.sock 27171486               * 0 
# 我设置了owan_web.sock的listen backlog是13 ,这里正确显示了

sudo netstat -a -p --unix|grep owan
unix  2      [ ACC ]     STREAM     LISTENING     27171486 5424/uwsgi          /tmp/owan_web.sock
unix  3      [ ]         STREAM     CONNECTING    0        -                   /tmp/owan_web.sock
unix  3      [ ]         STREAM     CONNECTING    0        -                   /tmp/owan_web.sock
unix  3      [ ]         STREAM     CONNECTING    0        -                   /tmp/owan_web.sock
unix  3      [ ]         STREAM     CONNECTING    0        -                   /tmp/owan_web.sock
# 也是类似根据pid查询进程的状态, 多个请求的情况下,有CONNECTED, CONNECTING等状态

Linux 2.4.7 a backlog of 3 given to listen() results in up to 6 connections being queued.
经测试:当uwsgi 监听的时unix sockets时候, listen 的backlog设置成5的话, 用ab压测并发数是不可以超过5+3
否认会报错:

 connect() to unix:/tmp/owan_web.sock failed (11: Resource temporarily unavailable) while connecting to upstream

ab -c 132 -n 1000 http://runmo.ouwan.com/mobi/essayDetail/?id=3
压测结果:挺正常的

Server Software:        nginx/1.4.6
Server Hostname:        mo.ouwan.com
Server Port:            80

Document Path:          /mobi/essayDetail/?id=3
Document Length:        29903 bytes

Concurrency Level:      130
Time taken for tests:   2.668 seconds
Complete requests:      122
Failed requests:        0
Total transferred:      3666466 bytes
HTML transferred:       3648166 bytes
Requests per second:    45.72 [#/sec] (mean)
Time per request:       2843.376 [ms] (mean)
Time per request:       21.872 [ms] (mean, across all concurrent requests)
Transfer rate:          1341.83 [Kbytes/sec] received

但是当uwsgi 监听的是端口IP的时候,runserver 的backlog默认5, ab压测可以远远超过5


nginx 高并发优化
系统内核层面:
net.core.somaxconn = 4096 允许等待中的监听
net.ipv4.tcp_tw_recycle = 1 tcp连接快速回收
net.ipv4.tcp_tw_reuse = 1 tcp连接重用
net.ipv4.tcp_syncookies = 0 不抵御洪水攻击
ulimit -n 30000

Nginx层面:
解决: nginx.conf 下面: work_connection 加大
worker_connections 10240;
Worker_rlimit_nofiles 10000;
Keepalive_timeout 0;


web 服务器使用什么并发策略,是影响最大并发数的关键


uwsgi: your server socket listen backlog is limited to 100 connections

Note that a "listen backlog" of 100 connections doesn't mean that your server can only handle 100 simultaneous (or total) connections - this is instead dependent on the number of configured processes or threads. The listen backlog is a socket setting telling the kernel how to limit the number of outstanding (as yet unaccapted) connections in the listen queue of a listening socket. If the number of pending connections exceeds the specified size, new ones are automatically rejected. A functioning server regularly servicing its connections should not require a large backlog size.


upstream prematurely closed connection while reading response header from upstream,
104: Connection reset by peer


http://www.cppblog.com/thisisbin/archive/2010/02/07/107444.html
http://www.jianshu.com/p/e6f2036621f4
https://stackoverflow.com/questions/12893379/listen-queue-length-in-socket-programing-in-c


UNIX Domain Socket 与 TCP/IP Socket 对比

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。
虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),
但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。
UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。
这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。
UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

A UNIX socket is an inter-process communication mechanism that allows bidirectional data exchange between processes running on the same machine.

IP sockets (especially TCP/IP sockets) are a mechanism allowing communication between processes over the network.
In some cases, you can use TCP/IP sockets to talk with processes running on the same computer (by using the loopback interface).

UNIX domain sockets know that they’re executing on the same system, so they can avoid some checks and operations (like routing);
which makes them faster and lighter than IP sockets.
So if you plan to communicate with processes on the same host, this is a better option than IP sockets.

strace 跟踪进程命令

strace -tfp PID
strace -o output.txt cmd
strace -o output.txt -fp PID
 #统计命令各个系统条用的次数
strace -c  command
# 通过使用-o选项可以把strace命令的输出结果保存到一个文件中。
sudo strace -o process_strace -p 3229
# strace命令的-e选项仅仅被用来展示特定的系统调用(例如,open,write等等)
strace -e open cat dead.letter

命令相关:1.https://lesliezhu.github.io/2014/06/19/strace%E5%91%BD%E4%BB%A4%E6%9F%A5%E7%9C%8B%E8%BF%9B%E7%A8%8B%E7%9A%84%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8/
2.https://linux.cn/article-3935-1.html

上一篇下一篇

猜你喜欢

热点阅读