处理多个插座连接

Dav*_*cía 5 python sockets client-server

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

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

编辑:我更新了问题,以解释问题稍微好一点,但主要是为了解决同样问题的人.在@TimMcNamara答案中实际上有一个很好的解决方案.

Tim*_*ara 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)
Run Code Online (Sandbox Code Playgroud)

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

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

非阻塞网络

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

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

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

为避免这种情况,已创建基于事件的库.当没有工作要做时,它们将以0%运行CPU,仅在要读取数据时才激活.在Python世界中,Twisted,Tornadogevent是大玩家.但是,有很多选择.特别是柴油看起来有吸引力.

以下是Tornado网页的相关摘录:

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

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

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

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

调整服务器

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

$ 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作为您的Web服务器.这应该可以让您了解为提高该限制可以进行的修改:

httpd soft nofile 20480
httpd hard nofile 20480
Run Code Online (Sandbox Code Playgroud)

对于极高的数量,您可能会达到系统范围的限制.您可以通过cat /proc/sys/fs/file-max以下方式查看

$ cat /proc/sys/fs/file-max
801108
Run Code Online (Sandbox Code Playgroud)

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