使用Python编写基于socket的服务器,有哪些推荐策略?

9
我最近阅读了this document,其中列出了一些实现套接字服务器的策略。它们分别是:
  1. 使用非阻塞I/O和水平触发就绪通知,为每个线程服务多个客户端
  2. 使用非阻塞I/O和就绪状态改变通知,为每个线程服务多个客户端
  3. 使用异步I/O,为每个服务器线程服务多个客户端
  4. 使用阻塞I/O,为每个服务器线程服务一个客户端
  5. 将服务器代码构建到内核中
现在,我想知道在CPython中应该使用哪种方法,我们知道它有一些优点和缺点。我主要关注高并发下的性能,目前一些实现过慢。
所以首先,"5"不行,因为我不会往内核里面添加任何东西。
"4"看起来也不行,因为GIL的原因。当然,你可以使用多进程代替线程,这确实会带来显著的提升。阻塞I/O也具有更易于理解的优点。

我的知识在这里有些欠缺:

"1" 是传统的 select 或 poll,可以轻松地与 multiprocessing 结合使用。

"2" 是就绪状态变更通知,由较新的 epoll 和 kqueue 使用。

"3" 我不确定是否有任何内核实现具有 Python 包装器。

因此,在 Python 中,我们拥有像 Twisted 这样的一袋伟大的工具。也许它们是一个更好的方法,尽管我已经对 Twisted 进行了基准测试,并发现它在多处理器机器上太慢了。也许有 4 个 twisteds 和负载均衡器可能会做到这一点,我不知道。任何建议都将不胜感激。

6个回答

7

asyncore基本上是“1”-它在内部使用select,而您只需要一个线程处理所有请求。根据文档,它也可以使用poll。(编辑:删除了Twisted参考,我以为它使用了asyncore,但我错了)。

“2”可以使用python-epoll实现(只是通过Google发现的-以前没见过)。编辑:(来自评论)在Python 2.6中,选择模块内置了epoll、kqueue和kevent(在支持的平台上)。因此,您不需要任何外部库来进行边缘触发服务。

不排除“4”的可能性,因为当线程实际执行或等待IO操作时,GIL将会被丢弃(很大一部分时间可能都是这样)。当然,如果您有大量连接,这是没有意义的。如果您有大量处理要完成,则使用任何这些方案可能都没有意义。

为了灵活性,也许可以看看Twisted

实际上,您的问题归结为您将为请求执行多少处理。如果您有很多处理要完成,并且需要利用多核并行操作,则可能需要多个进程。另一方面,如果您只需要监听大量连接,则select或epoll,使用少量线程即可工作。


我认为epoll在2.6+的stdlib中,并且可以通过easy_install安装到2.5。该软件包称为select-something。抱歉表述含糊。 - Ali Afshar
Twisted 也可以使用 epoll。事实上,Twisted 将所有支持的事件通知 API 转换为统一的 API,并呈现给您。因此,如果平台最好的选择是 select,则您的应用程序将使用 select。如果它有 epoll,则您的应用程序将使用 epoll。这一切对您来说都是透明的。 - Jean-Paul Calderone
@new123456,你说得对,之前没有人指出过这一点。 - Douglas Leeder

3

一种解决方案是使用gevent。 Gevent 将基于libevent的事件轮询与由greenlet实现的轻量级协作式任务切换相结合。

您将获得事件系统的所有性能和可扩展性,同时还具有阻塞IO编程的优雅和直观模型。

(我不知道关于回答非常古老问题的SO惯例是什么,但我决定仍然添加我的意见)


3
如何使用“fork”?(我认为这就是ForkingMixIn所做的)如果请求在“共享无关”的架构中处理(除了数据库或文件系统),则在大多数*nix上fork()会很快启动,您不必担心来自线程的所有愚蠢错误和复杂性。在我看来,线程是由于操作系统过重的进程而被强制施加的设计疾病。克隆具有写时复制属性的页面表似乎是一个小代价,特别是如果您正在运行解释器。抱歉我不能更具体,但我更多地是一个从Perl转向Ruby的程序员(当我不在工作中努力编写大量的Java代码时)。

更新:我最终在我的“空闲时间”中对线程和fork进行了一些计时。请查看:

http://roboprogs.com/devel/2009.04.html

Expanded: http://roboprogs.com/devel/2009.12.html


此外,在启动子进程之前,您可以拉取其他模块的代码,这样可以防止它们被反复标记化(JIT-ed等)。其次,保持父进程中的数据较小,使用“exit”作为超级垃圾收集器。 - Roboprog

2

我可以建议添加其他链接吗?

cogen 是一个跨平台的库,使用来自Python 2.5的增强生成器进行基于协程的网络编程。在cogen项目的主页上有链接指向几个类似目的的项目。


1

谢谢,但你有对这些东西进行基准测试吗?它们很慢。如果不必要,我不会重复发明轮子。 - Ali Afshar

1

我喜欢Douglas的回答,但是另外...

您可以使用一个集中式调度线程/进程,使用 select 监听准备通知并委托给一个工作线程/进程 池来帮助实现您的并行目标。

正如Douglas所提到的,GIL在大多数长时间的I/O操作期间不会被持有(因为没有Python-API的事情发生),所以如果您担心响应延迟,可以尝试将代码的关键部分移动到CPython API。


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