高并发下的第一个参数 backlog相关文章

转载http://i.iivey.com/thread-909-1-1.html

2013年12月14发布的PHP5.5.6中, changelog 中有一条变更,

FPM:Changed default listen() backlog to 65535.

这条改动,是在10月28日改的,patch提交者认为,提高backlog数量,哪怕出现timeout之类错误,也比因为backlog满了之后,悄悄的忽略TCP SYN的请求要好。

当我最近开始想好好了解一下backlog的细节问题时,发现fpm的默认backlog已经不是65535了,现在是511了。(没写在changelog中,你没注意到,这不怪你。)我翻阅了github提交记录,找到这次改动,是在2014年7月22日, Set FPM_BACKLOG_DEFAULT to 511


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


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 之类 )中可以修改。


大约了解其参数代表意义之后,我稍微测试了一番,并抓去了部分数据,首先确认系统默认参数

root@vmware-cnxct:/home/cfc4n# cat /proc/sys/net/core/somaxconn
128
root@vmware-cnxct:/home/cfc4n# ss -lt
State      Recv-Q Send-Q         Local Addressort                    Peer Addressort
LISTEN     0      128                        *:ssh                           *:*
LISTEN     0      128                 0.0.0.0:9000                           *:*
LISTEN     0      128                       *:http                           *:*
LISTEN     0      128                       :::ssh                           :::*
LISTEN     0      128                      :::http                           :::*
在FPM的配置中,listen.backlog值默认为511,而如上结果中看到的Send-Q却是128,可见确实是以/proc/sys/net/core/somaxconn跟listen参数的最小值作为backlog的值。

cfc4n@cnxct:~$ ab -n 10000 -c 300 http://172.16.218.128/3.php
This is ApacheBench, Version 2.3 <$Revision: 1604373 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 172.16.218.128 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:        nginx/1.4.6
Server Hostname:        172.16.218.128
Server Port:            80

Document Path:          /3.php
Document Length:        55757 bytes

Concurrency Level:      300
Time taken fortests:   96.503 seconds
Complete requests:      10000
Failed requests:        7405
   (Connect: 0, Receive: 0, Length: 7405, Exceptions: 0)
Non-2xx responses:      271
Total transferred:      544236003 bytes
HTML transferred:       542499372 bytes
Requests per second:    103.62 [#/sec] (mean)
Time per request:       2895.097 [ms] (mean)
Time per request:       9.650 [ms] (mean, across all concurrent requests)
Transfer rate:          5507.38 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    9  96.7      0    1147
Processing:     8 2147 6139.2    981   60363
Waiting:        8 2137 6140.1    970   60363
Total:          8 2156 6162.8    981   61179

Percentage of the requests served within a certain time(ms)
  50%    981
  66%   1074
  75%   1192
  80%   1283
  90%   2578
  95%   5352
  98%  13534
  99%  42346
100%  61179 (longest request)
apache ab这边的结果中,非2xx的http响应有271个,在NGINX日志数据如下:

root@vmware-cnxct:/var/log/nginx# cat grep.error.log|wc -l
271
root@vmware-cnxct:/var/log/nginx# cat grep.access.log|wc -l
10000
root@vmware-cnxct:/var/log/nginx# cat grep.access.log|awk '{print $9}'|sort|uniq -c
   9729 200
    186 502
     85 504
root@vmware-cnxct:/var/log/nginx# cat grep.error.log|awk '{print $8  $9  $10 $11}'|sort |uniq -c
    186 (111: Connection refused) while
     85 out (110: Connection timed
从nginx结果中看出,本次压测总请求数为10000。http 200响应数量9729个;http 502 响应数量186个;http 504响应数量未85个;即非2xx响应总数为502+504总数,为271个。同时,也跟error.log中数据吻合。同时,也跟TCP数据包中的RST包数量吻合。


backlog大小设置为多少合适?

从上面的结论中可以看出,这跟FPM的处理能力有关,backlog太大了,导致FPM处理不过来,nginx那边等待超时,断开连接,报504 gateway timeout错。同时FPM处理完准备write 数据给nginx时,发现TCP连接断开了,报“Broken pipe”。backlog太小的话,NGINX之类client,根本进入不了FPM的accept queue,报“502 Bad Gateway”错。所以,这还得去根据FPM的QPS来决定backlog的大小。计算方式最好为QPS=backlog。对了这里的QPS是正常业务下的QPS,千万别用echo hello world这种结果的QPS去欺骗自己。当然,backlog的数值,如果指定在FPM中的话,记得把操作系统的net.core.somaxconn设置的起码比它大。另外,ubuntu server 1404上/proc/sys/net/core/somaxconn 跟/proc/sys/net/ipv4/tcp_max_syn_backlog 默认值都是128,这个问题,我为了抓数据,测了好几遍才发现。

 

php-fpm backlog参数潜在问题(转载)

2013年12月09日 ⁄ 综合 ⁄ 共 2644字 ⁄ 字号 小 中 大 ⁄ 评论关闭

 

       前几天有业务在新机器上线测试时,发现个问题:同样的资源的虚机、同样配置的ngxin+php-fpmweb后端的两台机器,测试后发现:访问.html文件时QPS相差不大,但是访问php

页面时其中一台的QPS是另一台的数倍。通过分析,发现是php-fpm的backlog参数引起的,可以通过设置php-fpm.conf中的backlog参数来解决。下面是对问题的简单分析。

 

一、问题分析

       1、通过测试分析,的确存在所述的性能相差数倍的问题,因为访问静态文件的性能相当,所以可以确定排除nginx错误的可能,猜想是不是有php执行过慢导致呢?

于是安装了我们针对php5.2.5开发的slow log调试模块后发现没有执行慢的地方,然后把目光放到了nginx 与php建立连接的阶段上,使用tcpdump在服务器上抓包,

发现性能差的机器上存在大量的SYN3秒超时,并且会伴有请求头的超时重传。如下图:

        看来凶手已经找到了:是SYN 超时。一般SYN 超时是由于服务端backlog引起的,在我们的应用中,nginx –> php-fpm,所以php-fpm相当于服务端,查看php-fpm配置发现 backlog值是 -1 !!

gdb 跟踪fpm启动过程发现fpm没有对-1进行处理,而是直接把 -1 赋给了listen 系统调用,抱着好奇的心下载了linux2.6.18内核源码,跟踪listen系统调用执行过程如下:

asmlinkage long sys_listen(int fd, intbacklog)        {

if ((sock = sockfd_lookup_light(fd, &err, &fput_needed))!= NULL) {

         if((unsigned) backlog >sysctl_somaxconn)               //unsigned 把backlog变成了一个32位的最大整数:4294967295

             backlog =sysctl_somaxconn;                                    //也就是说当backlog=-1时,在内核中backlog被赋值为/proc/sys/net/core/somaxconn 的值,本机上为262144

      err =security_socket_listen(sock, backlog);

        if (!err)

                  err= sock->ops->listen(sock, backlog);

        fput_light(sock->file,fput_needed);

        }

         return err;

}

 

        抱着试试看的心态,改变了fpm配置backlog的值,测试发现把php-fpm的backlog值设为:10 –262143 之间机器的性能恢复了(1-10因为太小,所以性能不太理想),CPU跑得很high,但是

只要大于262144,性能就又变差了。结合上面的问题,SYN超时一般是服务器端完成连接队列满导致的, 既然backlog值被设置成了somaxconn,那么不应该出现内核中完成连接队列满的情况。

    为了搞清楚backlog值对tcp监听套接字的影响,编写了一个测试程序:服务端listen之后不accept,客户端循环来连接(服务端非阻塞)。

(1)把backlog设置为-1 或 大于262144的一个值时,客户端连接很慢,抓包发现有SYN 3、6秒超时,服务器端ESTABLISHED的连接也很少,如图:

 

(2)把backlog设置为n(10 <n < 262143)时,客户端瞬间就建立了n个连接(n到达ulimit -a的open files上限时报Too many open files 错误后客户端退出)。

 

        从上面的测试我们可以认为php-fpm是因为没有及时accept连接导致服务器不再接收TCP连接导致的,那么fpm为什么会不及时accept呢? 

原来fpm 多个进程是监听同一个套接字的,通过一个套接字锁来保证同一时刻只有一个进程可以accept,多进程间抢锁是需要消耗时间的,

在backlog被设置成-1的情况下,如果fpm没有及时accept,那么在并发量很大的情况下势必会出现SYN 超时重连了。

 

二、结论

        综上:性能差是由于php-fpm backlog参数设置为-1,导致fpm没能及时取出完成连接队列的socket,出现SYN 超时,最终导致压不上去,表现出性能差。

所以安装php-fpm时backlog一定要重新设置,不能用fpm默认配置的-1 ,可以根据机器的并发量来设置,建议设置在1024以上,最好是2的幂值(因为内核会调整成2的n次幂)。

如果您的业务机是2.6.18内核,同时发现php 机器性能特不合理,那么就试试改一下fpm的backlog参数吧,您肯定会震惊的。。。。

 

三、备注

       在2.6.32内核上测试就不会出现这个问题,因为2.6.32内核给listen socket分配空间时做了特殊的处理:

int reqsk_queue_alloc(struct request_sock_queue*queue,unsigned int nr_table_entries){

//…

   nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog);

   nr_table_entries = max_t(u32, nr_table_entries,8);

   nr_table_entries =roundup_pow_of_two(nr_table_entries + 1);

  lopt_size += nr_table_entries * sizeof(structrequest_sock *);

//…

}

nr_table_entries 可以认为是用户空间传进来的值,min_t保证了最大只取sysctl_max_syn_backlog的值,系统中是8196。

 

四、遗留问题

        虽然问题解决了,但是还存在两个疑问。

        1、虽然通过测试可以确定backlog设置很大会出现SYN超时,但是还不能从原理上解释?

         2、为什么同样设置的-1,有的机器性能很好,有的却很差呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值