处理多个插座连接

时间:2012-10-13 22:36:21

标签: python sockets client-server

我正在用Python编写客户端 - 服务器应用程序。我们的想法是拥有一台主服务器和数千个将与之连接的客户端。服务器将随机发送小文件到要处理的客户端,客户端必须每分钟完成工作并将其状态更新到服务器。我的问题是,目前我只有一个小而老的家庭服务器,所以我认为它无法处理这么多连接。也许你可以帮我解决这个问题:

  • 如何增加服务器中的连接数?
  • 如何平衡客户端的负载?
  • 我怎样才能改善沟通?我的意思是,我需要在服务器上有一个客户端列表及其状态(可能在数据库中?),这些更新将不时收到,所以我不需要永久连接。使用UDP发送更新是个好主意吗?如果没有,每次收到更新时是否必须创建新线程?

编辑:我更新了问题,以便更好地解释这个问题,但主要是为那些有同样问题的人提供足够的清晰度。在@TimMcNamara答案中实际上有一个很好的解决方案。

2 个答案:

答案 0 :(得分:14)

为成功做好准备:访问模式很重要

哪些设计决策可能会影响您实施网络解决方案的方式?你立即开始列出一些:

  • 可编程
  • 可用内存
  • 可用处理器
  • 可用带宽

这看起来很棒。我们想要一些容易编程的东西,而且规格相当高。但是,这个列表失败了。我们在这里做的只是看服务器。这可能是我们在Web应用程序中可以控制的全部内容,但是我们可以完全控制的分布式系统如传感器网络呢?

假设我们有10,000台设备想要用最新的传感器读数更新您,他们每分钟都会读取一次。现在,我们可以使用一个高端服务器来保存与所有设备的并发连接。

然而,即使您拥有一台极高端的服务器,您仍然可能会遇到性能问题。如果设备都使用相同的时钟,并且所有设备都在一分钟的时间内尝试发送数据,那么服务器将在每分钟1-2秒内完成大量的CPU工作,其余部分则没有。非常低效。

由于我们可以控制传感器,我们可以让他们自己进行负载平衡。一种方法是为每个设备提供一个ID,然后使用模数运算符仅在每分钟的正确时间发送数据:

import time        

def main(device_id):
    data = None
    second_to_send = device_id % 60
    while 1:
        time_now = time.localtime().tm_sec
        if time_now == 0:
            data = read_sensors()
        if time_now == second_to_send and data:
            send(data)
        time.sleep(1)

这种负载平衡的一个后果是我们不再需要这样的高功率服务器。我们认为我们需要与每个人保持联系的内存和CPU不是必需的。

我在这里要说的是,您应该确保您的特定解决方案专注于整个问题。通过您提供的简要描述,我们似乎不需要一直保持大量的连接。但是,我们确实需要100%连接。我们有什么选择?

非阻塞网络

非阻塞I / O的影响意味着当没有立即返回时,向文件描述符请求数据的函数。对于网络,这可能是坏的,因为尝试从套接字读取的函数将不向调用者返回任何数据。因此,有时产生一个线程然后调用read可能会简单得多。这样在线程内部阻塞不会影响程序的其余部分。

线程问题包括内存效率低下,线程创建所涉及的延迟以及与上下文切换相关的计算复杂性。

要利用非阻塞I / O,您可以在while 1:循环中对每个相关文件描述符进行自我轮询。这将是很好的,除了CPU将以100%运行的事实。

为避免这种情况,已创建基于事件的库。当没有工作要做时,它们将以0%运行CPU,仅在要读取数据时才激活。在Python世界中,TwistedTornadogevent是重要的参与者。但是,有many options。特别是diesel看起来很有吸引力。

以下是龙卷风网页的相关摘录:

  

因为它是非阻塞的并使用epoll或kqueue,它可以处理数千个同时站立的连接,这意味着它是实时Web服务的理想选择。

每个选项都采用略有不同的方法。 Twisted和Tornado在他们的方法上非常相似,依赖于非阻塞操作。 Tornado专注于Web应用程序,而Twisted社区则对更广泛的网络感兴趣。随后有更多用于非HTTP通信的工具。

gevent是不同的。该库修改了套接字调用,因此每个连接都在一个非常轻量级的类似线程的上下文中运行,尽管实际上这对于程序员来说是隐藏的。只要有阻塞调用,例如数据库查询或其他I / O,gevent就会很快切换上下文。

每个选项的结果是您可以在单个OS线程中为许多客户端提供服务。

调整服务器

您的操作系统对允许的连接数施加限制。如果达到您正在谈论的数字,您可能会达到这些限制。特别是,Linux在/etc/security/limits.conf中为每个用户维护限制。您可以通过在shell中调用ulimit来访问用户的限制:

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 63357
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 63357
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

我在这里鼓励了最相关的一行,open files。打开外部连接被视为打开文件。一旦达到1024限制,没有应用程序将能够打开另一个文件,也不会有任何其他客户端能够连接到您的服务器。我们假设您有一个用户httpd作为您的网络服务器。这应该让您了解可以为提高该限制所做的修改:

httpd soft nofile 20480
httpd hard nofile 20480

对于极高的音量,您可能会达到系统范围的限制。您可以通过cat /proc/sys/fs/file-max

查看它们
$ cat /proc/sys/fs/file-max
801108

要修改此限制,请使用sudo sysctl -w fs.file-max=n,其中n是您希望允许的打开文件数。修改/etc/sysctl.conf以使其重新启动。

答案 1 :(得分:1)

一般来说,即使是非常适度的家庭服务器,也可以同时拥有数万个套接字。

请确保不为每个连接创建新的线程或进程。