socket.listen(backlog) 中 backlog 指的是什么

1.首先,创建 socket bind 并 listen 之后,服务端就可以独自完成三次握手了(抓包确认),调用 accept 才是把「连接」从队列里取出来。

#-*- encoding: UTF-8 -*-

from socket import *

HOST = ''
PORT = 80
BUFSIZ = 1024
ADDR = (HOST, PORT)

tcpSerSock = socket(AF_INET, SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(2)

while True:
    pass
    # print 'waiting for connection...'
    # tcpCliSock, addr = tcpSerSock.accept() # 等待连接
    # print '...connected from:', addr

tcpSerSock.close()

2.下面我们看看假如不调用 accept,最大能有多少 ESTABLISHED 的连接。

server 端代码还是采用上面的,client 代码如下:

#-*- encoding: UTF-8 -*-

import time
from socket import *
from multiprocessing.dummy import Pool as ThreadPool
HOST = '10.19.28.33'
PORT = 80
ADDR = (HOST, PORT)
MAX_THREAD_NUM = 100

def connect(x):
    tcpCliSock = socket(AF_INET, SOCK_STREAM)
    tcpCliSock.connect(ADDR) # 套接字连接
    time.sleep(4)
    return time.time(), tcpCliSock

pool = ThreadPool(MAX_THREAD_NUM)
result_data = pool.map(connect, xrange(MAX_THREAD_NUM))
pool.close()
pool.join()

print result_data

测试结果如下:

1). 当 server 端 listen 的 backlog 为0,最大的 ESTABLISHED 数量为 2;

2). 当 listen 的 backlog 为1,最大的 ESTABLISHED 数量为 2;

3). 当 listen 的 backlog 为 N,则最大的 ESTABLISHED 数量为 N + 1;

所以,backlog 代表着有多少个已经建立的但是没有被 accept 取走的连接数量。

另外,有个内核参数 net.core.somaxconn 定义了 socket 同时 listen 的最大连接数,所以 socket.listen() 指定的值不能超过 net.core.somaxconn。


3.如果 ESTABLISHED 满了,此时进来的连接请求状态是 SYN_RECV,那么 SYN_RECV 的数量(也就是 SYN 队列长度)最大是多少呢?

答案是由内核参数 net.ipv4.tcp_max_syn_backlog 决定。

我做了个测试,环境如下:

server 端,ip 10.19.28.33,代码如1,listen backlog 为2,net.ipv4.tcp_max_syn_backlog 设置为8,net.ipv4.tcp_syncookies 设置为0。

clinet 端,ip 10.0.11.12,代码如2,MAX_THREAD_NUM 设置为12。

两台机器的 /proc/sys/net/ipv4/tcp_synack_retries 和 /proc/sys/net/ipv4/tcp_syn_retries 都为2,而且 /proc/sys/net/ipv4/tcp_abort_on_overflow 都为0。

client 端会报:: [Errno 110] Connection timed out

在 server 端抓包,包在这里: socket.pcap

这里不纠结发生了什么,在后面的文章继续。

Nginx 单IP下 配置多个server https 的问题

很早之前就遇到类似的问题,今天写下来以防忘记。


我们的负载均衡架构是这样的:

请求 —> 网络 —> LVS集群 —> Nginx 集群 —> APP

LVS使用FULLNAT模式,每台Nginx 机器只有一个IP(内网IP),LVS也是把流量转到这个IP。如果Nginx想对多个域名使用 https,比如两个域名 wandoujia.com 和 wandoulabs.com ,是可能有问题的。


看下面的配置(两个server写在不同文件中,用 include * 加载):

server {
    listen 80;
    listen 443 ssl;
    server_name test.wandoujia.com;
    ssl_certificate wandoujia.crt;
    ssl_certificate_key wandoujia.key;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;
    …
}

server {
    listen 80;
    listen 443 ssl;
    server_name test.wandoulabs.com;
    ssl_certificate wandoulabs.crt;
    ssl_certificate_key wandoulabs.key;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;
    …
}

这样你访问 https://test.wandoujia.com 或者 https://test.wandoulabs.com,都可能报证书有问题,这是为什么。

事实上,SSL运行在TCP之上(SSL/TLS协议),ssl通过四次握手和服务器(这里是Nginx,LVS纯转发,可忽略)的IP + PORT(443)建立ssl连接,建立连接之后浏览器才会发送HTTP请求。所以在Nginx收到HTTP请求之后才知道Host,才知道转到哪个server 去处理,所以在SSL连接建立的时候Nginx是不知道用哪个 Server 的SSL配置的,在这种情况下,Nginx会使用它加载到的第一个SSL配置(需验证)。当然如果你在listen 443的后面加上 default_server,Nginx就会使用此SSL配置,即:

listen 443 default_server ssl;

那么对于单域名的https,我更喜欢把SSL配置写在http配置里,在server只需要加上 listen 443 ssl;,类似:

http {
    …
    ssl_certificate wandoujia.crt;
    ssl_certificate_key wandoujia.key;
    #ssl_certificate wandoulabs.crt;
    #ssl_certificate_key wandoulabs.key;
    ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!MD5;
    …
}

server {
    listen 80;
    listen 443 ssl;
    server_name test.wandoujia.com;
    …
}

那么怎么才能实现多域名的 https 呢,是有办法的,叫做 TLS Server Name Indication extension(SNI, RFC 6066),它允许浏览器在SSL握手的时候发送请求的server name,也就是 Host,这样 Nginx 就能找到对应server 的SSL配置。

但是要浏览器支持SNI 才可以,支持SNI的浏览器有:

Opera 8.0;
MSIE 7.0 (but only on Windows Vista or higher);
Firefox 2.0 and other browsers using Mozilla Platform rv:1.8.1;
Safari 3.2.1 (Windows version supports SNI on Vista or higher);
and Chrome (Windows version supports SNI on Vista or higher, too).

同样,在服务器端 openssl 要支持SNI,编译的时候加上–enable-tlsext 即可,不过从 0.9.8j 版本开始编译的时候默认会加。openssl 支持了SNI,Nginx 才可以work,确认是否OK:

$ ./nginx -V
…
TLS SNI support enabled
…

所以,如果想要实现多域名https,要确认你的Nginx 支持 TLS SNI support,然后在server 单独配置SSL, 但是要注意,某些浏览器会有问题,而且因为Nginx加载server 的顺序不同(特别是每个server在同一目录的不同文件中,然后在nginx.conf主配置文件中用 include * 包含),可能会出现奇怪的问题,我是遇到过的。