如何保持一百万个同时的TCP连接?

42

我需要设计一台服务器,可以同时通过TCP连接数百万个客户端。

由于服务器和客户端之间的数据流量较少,因此可以忽略带宽问题。

一个重要的要求是,每当服务器需要向任何客户端发送数据时,都应该使用现有的TCP连接,而不是向该客户端开启一个新的连接(因为客户端可能在防火墙后面)。

有人知道如何做到这一点,并且需要什么硬件/软件(成本最低)吗?


2
它必须是TCP吗?如果流量稀少,跟踪所有连接的成本可能会不合理高。您能详细说明一下服务器将要做什么吗?对我来说,拥有数百万个未被积极使用的活动连接似乎有些可疑。 - VladV
6个回答

21

你们考虑采用哪种操作系统?

如果使用的是Windows操作系统,并且使用的是比Vista更高的版本,那么在单台机器上处理成千上万个连接不应该有问题。我曾经在一台配置较低的Windows Server 2003机器上运行过测试(参见链接:http://www.lenholgate.com/blog/2005/11/windows-tcpip-server-performance.html),很容易就实现了超过70,000个活动TCP连接。一些影响连接数量的资源限制已经在Vista上得到了显著提升(请参见此处:http://www.lenholgate.com/blog/2005/11/windows-tcpip-server-performance.html),因此您可能可以通过一小组机器来实现您的目标。但我不知道您需要在这些机器前面放置什么来路由这些连接。

Windows提供了一个称为I/O完成端口的功能(请参见:http://msdn.microsoft.com/en-us/magazine/cc302334.aspx),它允许您使用非常少的线程服务于多达数千个并发连接(我昨天正在运行测试,使用5000个连接饱和了与服务器的链接,并使用2个线程来处理I/O...)。因此,基本架构非常具有可扩展性。

如果您想进行一些测试,我在我的博客上提供了一些免费工具,可以使用成千上万的连接来测试一个简单的回显服务器(参见:1)和(2),同时还提供了一些免费代码,可帮助你入门(3)。

您的问题第二部分,从您的评论中可以看出,更加棘手。如果客户端的IP地址不断变化,并且你们之间没有提供NAT以给你一个一致的IP地址,那么他们的连接无疑会被终止并需要重新建立连接。如果客户端在其IP地址更改时检测到此连接中断,则可以重新连接服务器;如果不能,则建议客户端每隔一段时间轮询服务器以便检测连接丢失并重新连接。这里服务器无能为力,因为它无法预测新的IP地址,并且当它尝试发送数据时会发现旧的连接已经失败。而且请记住,一旦系统扩展到这个级别,您的问题才刚刚开始...

1
你的EchoServerTest很好,但是我们如何测试超过64k个连接?客户端有64k端口限制。 - onmyway133
1
多个客户端机器。目前您必须手动完成此操作,因为我发现没有必要编写一个可以从单台计算机上运行并在其他计算机上作为从机运行的网络客户端测试(这是我要做的事情清单上的一项...)。 - Len Holgate
1
在Linux上,您还可以在客户端机器上使用别名IP地址;每个别名IP将为您提供额外的65k个客户端。 - George Y.
3
为什么神秘的投票者一天内突然给了两个反对票?为什么不添加评论来解释为什么你认为这个回答是错误的,这样我们可以讨论并改善回答,或者至少我可以学到一些东西。请注意,我的翻译目标是使内容更加通俗易懂,同时保持原意不变。 - Len Holgate
你的最后一行,还可能遇到哪些问题? - Zapnologica

11

这个问题与所谓的C10K问题有关。C10K页面列出了大量优秀资源,以解决当你尝试让数千个客户端连接到同一个服务器时会遇到的问题。


谢谢你的回答。但是我个人认为它们不是同一个问题。我想知道的是如何保持100万个连接客户端的持久连接,而不是如何接受他们的连接请求或如何检测他们的连接状态变化。无论如何,还是谢谢。 - cow
@cow:保持客户端连接并不特别困难——你为什么认为会有困难呢?这远非问题中最具挑战性的部分。 - caf
如果客户端在一个IP地址可能经常更改的网络中,会怎么样呢?例如,我有一部T-Mobile G1手机。我发现我的手机IP地址经常变化。即使手机与T-Moble网络外的某个服务器建立了TCP连接,当连接上没有数据流动时,手机IP地址被更改的机会很大;一旦IP地址更改,任何TCP连接实际上都会断开。这就是为什么我会遇到这个问题的原因。 - cow
据我所知,思科7600路由器仅支持高达64k个TCP连接,并且售价约为50,000美元。 如果我想要支持100万个同时连接的用户,我是否需要20台思科7600路由器,数十台Linux服务器和可能还有几个负载均衡器? - cow

4

我之前了解过APE项目。它似乎是梦想成真。它可以在单个节点上支持高达100k并发客户端。将它们分布在10或20个节点上,您就可以为数百万人提供服务。非常适合RESTful应用程序。可能需要深入了解任何共享命名空间。其中一个缺点是这是一个独立的服务器,作为Web服务器的补充。当然,这个服务器是开源的,所以任何费用都与硬件/ISP相关。


谢谢提供的信息。但是我的应用程序不是基于Web的。我如何利用APE? - cow
您可以在没有浏览器的情况下使用APE。它们使用默认协议(而您也可以创建自己的协议:http://www.ape-project.org/wiki/index.php/Protocol)。您可以编写自己的库来使用"APE Javascript framework"作为参考,使用您选择的语言管理连接。 - Vic
再次感谢。我会研究这个协议,看看是否可以用APE的协议替换我的自己编码的二进制协议。 - cow

1

你不能使用UDP。如果客户端发送请求,而你不立即回复,路由器将在30秒或更短时间内忘记反向路由,因此你的服务器将无法回复客户端。

TCP是唯一的选择,但它也会给你带来麻烦。大多数路由器会在几分钟后忘记路由和/或断开连接,因此你的客户端/服务器代码需要经常发送“保持活动”信号。

我建议设置一个“嗅探器”,以查看电话公司如何通过他们的“推送”技术与你的智能手机保持联系。复制他们正在做的任何事情,因为那些东西有效


路由器只需要进行路由,不需要为UDP设置反向路由,也不涉及TCP连接... 操作流量的中间设备,如防火墙或NAT设备则会这样做。(并且它们可能对可以跟踪的连接数量也有限制...) - Gert van den Berg

0
正如Greg所提到的,您描述的问题是C10K(或者在您的情况下是“C1M”) 我最近在Linux上制作了一个简单的TCP回显服务器,它可以很好地扩展会话数量(尽管只测试了200,000个),通过使用{{link1:epoll}}队列。在BSD上,您有类似的东西叫做kqueue。 如果您想要,可以查看{{link2:code}}。希望这可以帮助您,祝您好运!

-4

编辑:如下评论中所述,我最初的断言基于端口数量存在64K限制是不正确的,然而在socket句柄数量方面确实存在32K的限制,因此我的建议设计是有效的。

使用典型的TCP/IP服务器设计,你受到同时打开连接数量的限制。服务器有一个侦听端口,当客户端连接到它时,服务器进行接受调用,并为连接的其余部分创建一个新的Socket(随机端口)。

为了处理超过64K个并发连接,我认为您需要改用UDP。你只需要一个端口来侦听服务器,并且需要使用数据包中的32位客户端ID来管理连接,而不是为每个客户端单独分配一个端口。32位客户端ID可以是客户端的IP地址,客户端可以在已知的UDP端口上侦听从服务器返回的消息。该端口将是防火墙上唯一需要打开的端口。

采用这种方法,您唯一的限制是处理和响应UDP消息的速度。即使是稀疏的流量,在拥有数百万个客户端的情况下,也可能导致大量的峰值,并且如果您没有足够快地读取数据包,则输入队列将填满并且会开始丢包。Greg指出的C10K页面将为您提供解决策略。


2
多个客户端连接到服务器时不会使用额外的服务器端口。仍然会有技术限制,但并不是端口数量的问题。在服务器端,连接使用唯一的4元组(server_ip、server_port、client_ip、client_port)进行标识。您可能正在考虑套接字句柄,其中服务器套接字似乎通过accept()调用生成更多的套接字句柄。 - Greg Hewgill
嗯,是的,我想你是对的。http://linux.die.net/man/2/accept说如果你用尽了文件描述符而不是端口,accept将会失败。我查看了Linux内核源代码,从中可以看出最大文件数以int形式传递。这可能会限制您到32K个文件描述符,但它会因平台而异。关于TCP受限的细节我理解有误,但我认为它仍然受限,而UDP是一个可行的替代方案。 - DougWebb
感谢你们两位的评论。几周前我看到了C10K页面。我记得它建议使用epoll()。我希望我能在UDP中使用它。然而,据我所知,epoll与仅有一个接收套接字的UDP服务器无关,我是正确的吗?快速读取数据包的紧迫性是合理的。但是,我想不出比使用专用线程从UDP套接字读取数据包并将其放入队列以供工作线程消耗更快的方法。你能提供更好的方法吗?非常感谢。 - cow
3
这不正确。至少在Windows操作系统上,连接由主机地址、主机端口、远程地址和远程端口的元组来识别。因此,虽然你可能会受到出站连接端口数量的限制(因为你只能从单个暂时性端口范围中进行选择),但是你对来自不同主机的入站连接没有限制。我在一台低配置的Windows Server 2003机器上运行了测试,链接在这里:http://www.lenholgate.com/archives/000568.html,可以轻易地实现超过70,000个并发入站TCP连接。 - Len Holgate
@cow:如果你需要对每个数据包进行处理,将它们转换为队列以供工作线程消耗是正确的选择。你需要平衡工作线程的数量、它们所做的工作量和数据包的到达速率,并确保你的队列能够扩展以处理积压的数据包。我曾经遇到过类似的项目,面对突发性的数据流量,我将数据包写入文件中,在空闲期间进行批处理。 - DougWebb
@LenHolgate 正确。这是TCP的属性,不仅仅是Windows的属性。上面的说法,即服务器“在随机端口上创建一个新的套接字以进行其余连接”是完全错误的。另请参见此处 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接