python - 类似于telnet的服务器的推荐体系结构(多进程?进程池?)

标签 python python-3.x network-programming

我正在为类似telnet的协议编写Python服务器。客户端连接并验证会话,然后发出一系列命令,每个命令都有响应。会话具有状态,即用户进行一次身份验证,然后假设后续命令由该用户执行。不同会话中的命令/响应操作实际上是独立的,尽管它们确实涉及对共享的IO资源(postgres)的读取和偶尔写入,这些资源在很大程度上能够管理其自己的并发性。

一个设计目标是使用少量的8核或16核服务器来支持大量用户。我正在寻找一种合理有效的方法来构建服务器实现。

我考虑过的一些选项包括:


在每个会话中使用线程;我怀疑使用GIL会浪费可用的内核
每个会话使用多个进程;我怀疑会话与服务器的比率很高(例如1000比1),因此1000个python解释器的开销可能超出了内存限制。用户连接时,还会遇到“启动缓慢”的问题。
将会话分配给约32个进程的进程池;空闲会话可能会分配给所有32个进程,并阻止非空闲会话被处理。
使用某种类型的“路由”系统,其中所有会话都由单个进程处理,然后将各个命令移植到进程池中。对于我来说,这仍然听起来基本上是单线程的(因为存在很大的单线程瓶颈),如果某些命令非常琐碎,但必须两次跨越IPC边界并等待一个免费进程来获取该命令,则该系统可能会引入大量开销。响应。
使用Jython / IronPython和多线程;缺少C扩展名是一个问题
Python不太适合解决这个问题。使用Go / C ++ / Scala / Java作为Python进程的路由器或完全放弃Python。

最佳答案

在每个会话中使用线程;我怀疑使用GIL会浪费可用的内核


您的代码实际上是否受CPU限制?*如果花所有时间在I / O上等待,那么GIL根本就没有关系。**因此,绝对没有理由使用进程或无GIL的Python实现。



当然,如果您的代码受CPU限制,那么您绝对应该使用进程或无GIL的实现。但是在那种情况下,您实际上只能使用N个CPU一次有效地处理N个客户端,这与您要描述的问题完全不同。拥有10000名用户的所有人都在为在8个内核上运行CPU约束的代码而奋斗,这只会使他们沮丧。解决该问题的唯一方法是一次只处理8或32个,这意味着甚至不会出现整个“ 10000个并发连接”问题。

因此,我假设您的代码受I / O约束,并且您的问题是一个明智且可解决的问题。



还有其他原因可能会限制线程。特别是,如果您要处理10000个并发客户端,则您的平台可能无法运行10000个并发线程(或无法高效地在它们之间切换),因此这将无法工作。但是在那种情况下,流程通常也无济于事(实际上,在某些平台上,它们只会使事情变得更糟)。

为此,您需要使用某种异步网络-一个proactor(一个小的线程池和I / O完成),或一个Reactor(一个围绕I / O准备多路复用器的单线程事件循环)。 Python文档中的Socket Programming HOWTO显示了如何使用select执行此操作;使用更强大的机制来完成此任务要复杂一些,并且要针对特定​​平台,但要困难得多。

但是,有些库使这变得容易得多。 Python 3.4带有asyncio,***,它使您可以抽象出所有令人讨厌的细节,并只需编写与协程通信的协议即可。幕后有一个反应堆或一个proactor(每个平台都有一个好的反应堆),而您不必担心。

如果您不能等待3.4的定稿,或者想使用流血边缘较少的东西,可以使用流行的第三方框架,例如Twisted,它也具有其他优点。

或者,如果您更喜欢考虑线程范式,可以使用gevent之类的库,而使用greenlets来伪造反应堆顶部的单个套接字上的一堆线程。



从您的评论看来,您确实有两个问题:

首先,您需要处理10000个几乎无所事事的连接。如果尝试使用select之类的东西,实际调度和多路复用10000个连接本身就是一个主要的I / O约束,并且正如我所说,运行10000个线程或进程将无法正常工作。因此,您需要为平台使用良好的proactor或Reactor,以上已对此进行了描述。

其次,这些连接中的一些连接将同时存在。

首先,为简单起见,让我们假设它们全部受CPU限制。因此,您将需要流程。特别是,您需要一个由N个进程组成的池,其中N是核心数。您可以通过创建concurrent.futures.ProcessPoolExecutor()multiprocessing.Pool()来执行此操作。

但是您声称他们正在做CPU约束和I / O约束的混合工作。如果所有任务花费的时间是CPU的1/4,则使用4N进程。在上下文切换中有一些浪费的开销,但是您不太可能注意到它。您可以将N作为n = multiprocessing.cpu_count()获得;然后使用ProcessPoolExecutor(4*n)Pool(4*n)。如果它们不是那么一致或不可预测,则您仍然几乎总是可以假装是它们–测量一堆任务的平均CPU时间,并使用n/avg。您可以根据是更关注最大化峰值性能还是典型性能来向上或向下移动,但这只是旋转的一个旋钮,您也可以凭经验旋转。

就是这样。*****



*…以及在不发布GIL的Python或C扩展中。如果您使用的是例如NumPy,它将在不按住GIL的情况下完成许多缓慢的工作。

**嗯,这在Python 3.2之前很重要。但是希望如果您已经在使用3.x,则可以升级到3.2+。

***还有asyncore及其朋友asynchat,它们已经存在于stdlib中数十年了,但是最好还是忽略它们。

****例如,诸如Twisted之类的框架充斥着协议实现,包装器和适配器等,以绑定各种其他功能,而不必自己编写一堆复杂的代码。

*****如果确实还不够好,并且当所有任务恰巧同时处于I / O等待状态时任务切换开销或空闲状态会导致性能下降吗?好吧,除了特定种类的应用程序外,这两种情况都不太可能。如果发生这种情况,您将需要分解任务以将实际的CPU绑定子任务与I / O绑定分开,或者编写某种特定于应用程序的自适应负载平衡器。

关于python - 类似于telnet的服务器的推荐体系结构(多进程?进程池?),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20847185/

相关文章:

python - 如果存在另一个参数则需要参数

python - 使用 matplotlib 在另一个轴中嵌入多个插入轴

python - 正则表达式多个相同模式/重复捕获无法正常工作,仅匹配第一个和最后一个

python - 获取带有装饰器的函数的父类名

ubuntu - 启动 Raise 网络接口(interface)失败

java - 使用 java.net 在 Java 中创建 FTP 客户端 - 连接被拒绝 : connect

c# - 通过网络传输 int

Python乘法范围

python - Python 从哪里获取回溯信息?

python - 如何编写在变量或对象(例如 "string {0}".format(stringy))之后调用的方法?