04-Windows频繁打开和关闭端口可能引发的问题 | 07.杂项

04-Windows频繁打开和关闭端口可能引发的问题

郑昀 20100810 隶属于《07.杂项》小节

老赵写了一篇《关于Windows频繁打开关闭端口时出现的问题》,论述了他从 Windows Web Server 2008 R2 通过 TCP 连接对 Cent OS 下的 MongoDB 数据库服务做压力测试,遇到了 Socket 连接资源耗尽,导致程序报告“由于系统缓冲区空间不足或队列已满,不能执行套接字上的操作。”错误的情况。

Socket 连接资源耗尽,在 Windows Server 下很常见,如果使用者程序写得没问题的话,一般都是微软(或其他软件厂商)设置的一些默认参数不合时宜导致的。

我以前写过一篇《02-Twisted 构建 Web Server 的 Socket 长链接问题 | 07.杂项 | Python》,记述了另外一种 Windows Server 常见问题:在 Python 开发语言下,用 twisted 框架编写一个 Web Server ,接收 Google PubSubHubbub 的 Hub Server 推送过来的 Google Reader 文章共享信息时,遇到了文件描述符到达上限的现象。这个模式的特点是,用 twisted.web.server 对接 PubSubHubbub Hub Server ,双方都支持重用连接,照理说,既然保持长连接并且重用 socket 连接,不应该出现这种“too many file descriptors in select”错误。这里的知识点是 Windows 下 select module 文件描述符(file descriptor)最多是512个,而在 Linux 下这个限制为 32767 ,如果超过这个限制值,就会出现类似上面的异常。

 

老赵的文章指出了一个有意思的知识点:临时端口号可分配范围, 

临时端口号范围定义

当一个客户端程序(比如说一个浏览器)初始化一个 connection 连接远端服务(比如一个网站)时,客户端会打开一个“ephemeral(临时)”端口,端口号一般是随机分配的。你可以用微软工具 TCPView 来查看。

那么这个进程为出站连接(outbound connection)分配端口号时,端口号的可分配范围与操作系统有关。其中一个主要影响因素是 MaxUserPort ,位于注册表的 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 下。

下面这张表列出了不同 Windows 操作系统所能分配的端口范围:

操作系统

MaxUserPort
数值的
含义

端口范围,如果MaxUserPort
没有定义

端口范围,如果MaxUserPort
已定义

最小值

最大值

Windows NT/2000/XP/2003

Ending Port

1025 to 5000

1025 to [MaxUserPort]

5000

65535

Windows 2000/XP/2003 with KB951748/KB951746

Ending Port

49152 to 65535

1025 to [MaxUserPort]

5000

65535

Windows Vista/2008

Number of Ports

49152 to 65535

49152 to [49152 + MaxUserPort − 1]

255

16384

表1 端口范围

当然,你如果在服务器上安装了微软的一些服务,还是会自动修改这个 MaxUserPort 参数的,比如Small Business Server 2000/2003 会修改为 60000, ISA Server 会修改为 65535, Exchange Server 2003 会改为 60000 。

看了这张表后,你会知道:

1、不要使用小于1025的端口号;

2、尽量使用系统临时端口范围(ephemeral port range)之外的端口,比如你定义自己应用程序要打开的端口号为8000,而不是5000。

3、重用连接。

查看了我的笔记本电脑(Win XP)和服务器(Windows 2003),注册表里都没有设置过这个MaxUserPort参数,所以端口范围就是默认的,在服务器上,可分配的临时端口是从1025到5000。

 

回到我的文件描述符打开过多的问题上,

重用端口失败+保持长连接生效

如果监听的 Google Reader User 足够多,这些用户又在一个时段集中分享文章,那么 Google PubSubHubub Hub Server 就会以极快的速度把数据推送过来,此时如果重用 socket 端口失败,而保持长连接策略又起作用,于是很快在 twisted web server 监听的端口上打开的文件描述符又爆了。

处理办法:

1、参考《02-Twisted 构建 Web Server 的 Socket 长链接问题 | 07.杂项 | Python》,

twisted.web.server.Site 类的初始化函数有一个可选参数 timeout ,它的默认值是 60*60*12 ,也就是12小时,它的目的是闲置端口超时自动关闭。在我们的情景下,超时时间太长,所以才会有许多处于 ESTABLISHED 状态的 Socket Connections 积累。

所以我们缩短为 15 分钟(也可以更短),让没有得到重用的 Connections 尽快自动关闭。只需要在开始执行:
reactor.listenTCP(8080, Site(MyWebResource.setup(),timeout=60*15))
即可。

2、参考《03-PubSubHubbub 和 twisted 的 Persistent connections 能力 | 07.杂项 | Python》,接收到对方 Server 推送过来的数据后,异步扔给另一个方法解析和处理,render_POST 方法立刻 return NOT_DONE_YET 标志, 等异步处理数据成功了,回调里再 finish 掉当前的 request 。

 

参考资源:

1、关于Windows频繁打开关闭端口时出现的问题

2、Choosing a TCP Port for a Network Service

3、02-Twisted 构建 Web Server 的 Socket 长链接问题 | 07.杂项 | Python

4、03-PubSubHubbub 和 twisted 的 Persistent connections 能力 | 07.杂项 | Python

5、TCP - WAIT状态及其对繁忙的服务器的影响

posted @ 2010-08-10 16:59  旁观者  阅读(4419)  评论(2编辑  收藏  举报