用Python编写基于套接字的服务器,推荐策略?
我最近正在阅读此文档,其中列出了许多可用于实现套接字服务器的策略。 即,它们是:
- 使用每个线程为多个客户端提供服务,并使用非阻塞 I/O 和级别触发的就绪通知
- 使用每个线程为多个客户端提供服务,并使用非阻塞 I/O 和就绪更改通知
- 使用每个服务器线程为多个客户端提供服务,并且使用异步 I/O 使用
- 每个服务器线程为一个客户端提供服务,并使用阻塞 I/O
- 将服务器代码构建到内核中
现在,我希望得到关于在 CPython 中应该使用哪个的提示,我们将使用它知道有一些优点,也有一些缺点。 我最感兴趣的是高并发下的性能,是的,当前的许多实现都太慢了。
因此,如果我可以从简单的开始,“5”就被淘汰了,因为我不会对内核进行任何攻击。
“4”看起来也一定是因为 GIL 的缘故。 当然,您可以使用多处理来代替此处的线程,这确实会带来显着的提升。 阻塞IO还有一个优点是更容易理解。
在这里我的知识有点减弱:
“1”是传统的选择或轮询,可以与多处理简单地结合起来。
“2”是就绪更改通知,由较新的 epoll 和 kqueue 使用
“3”我不确定是否有任何具有 Python 包装器的内核实现。
所以,在 Python 中,我们有很多很棒的工具,比如 Twisted。 也许它们是更好的方法,尽管我对 Twisted 进行了基准测试,发现它在多处理器机器上太慢了。 我不知道,也许使用负载平衡器进行 4 条绞合可以做到这一点。 任何意见,将不胜感激。
I was recently reading this document which lists a number of strategies that could be employed to implement a socket server. Namely, they are:
- Serve many clients with each thread, and use nonblocking I/O and level-triggered readiness notification
- Serve many clients with each thread, and use nonblocking I/O and readiness change notification
- Serve many clients with each server thread, and use asynchronous I/O
- serve one client with each server thread, and use blocking I/O
- Build the server code into the kernel
Now, I would appreciate a hint on which should be used in CPython, which we know has some good points, and some bad points. I am mostly interested in performance under high concurrency, and yes a number of the current implementations are too slow.
So if I may start with the easy one, "5" is out, as I am not going to be hacking anything into the kernel.
"4" Also looks like it must be out because of the GIL. Of course, you could use multiprocessing in place of threads here, and that does give a significant boost. Blocking IO also has the advantage of being easier to understand.
And here my knowledge wanes a bit:
"1" is traditional select or poll which could be trivially combined with multiprocessing.
"2" is the readiness-change notification, used by the newer epoll and kqueue
"3" I am not sure there are any kernel implementations for this that have Python wrappers.
So, in Python we have a bag of great tools like Twisted. Perhaps they are a better approach, though I have benchmarked Twisted and found it too slow on a multiple processor machine. Perhaps having 4 twisteds with a load balancer might do it, I don't know. Any advice would be appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
asyncore
基本上是“1” - 它使用select
在内部,你只有一个线程处理所有请求。 根据文档,它还可以使用poll
。 (编辑:删除了 Twisted 参考,我认为它使用了 asyncore,但我错了)。“2”可以用 python-epoll 实现(只是用谷歌搜索 - 以前从未见过)。
编辑:(来自评论)在python 2.6中, select 模块 有 epoll、kqueue 和kevent 内置(在支持的平台上)。 因此,您不需要任何外部库来执行边缘触发服务。
不要排除“4”,因为当线程实际执行或等待 IO 操作时(可能大多数时间),GIL 将被删除。 当然,如果你有大量的连接,那就没有意义了。 如果您有大量处理要做,那么 python 可能对这些方案都没有意义。
为了灵活性,可以看看Twisted?
实际上,您的问题归结为您要对请求进行多少处理。 如果您有大量处理,并且需要利用多核并行操作,那么您可能需要多个进程。 另一方面,如果您只需要侦听大量连接,那么使用少量线程的 select 或 epoll 应该可以工作。
asyncore
is basically "1" - It usesselect
internally, and you just have one thread handling all requests. According to the docs it can also usepoll
. (EDIT: Removed Twisted reference, I thought it used asyncore, but I was wrong)."2" might be implemented with python-epoll (Just googled it - never seen it before).
EDIT: (from the comments) In python 2.6 the select module has epoll, kqueue and kevent build-in (on supported platforms). So you don't need any external libraries to do edge-triggered serving.
Don't rule out "4", as the GIL will be dropped when a thread is actually doing or waiting for IO-operations (most of the time probably). It doesn't make sense if you've got huge numbers of connections of course. If you've got lots of processing to do, then python may not make sense with any of these schemes.
For flexibility maybe look at Twisted?
In practice your problem boils down to how much processing you are going to do for requests. If you've got a lot of processing, and need to take advantage of multi-core parallel operation, then you'll probably need multiple processes. On the other hand if you just need to listen on lots of connections, then select or epoll, with a small number of threads should work.
“叉子”怎么样? (我假设这就是 ForkingMixIn 所做的)如果请求在“无共享”(数据库或文件系统除外)架构中处理,则 fork() 在大多数 *nix 上启动得相当快,您不必担心关于线程中所有愚蠢的错误和复杂性。
恕我直言,线程是进程过重的操作系统强加给我们的一种设计弊病。 克隆具有写时复制属性的页表似乎代价很小,特别是如果您无论如何都在运行解释器的话。
抱歉,我不能说得更具体,但我更像是一个从 Perl 过渡到 Ruby 的程序员(当我在工作中没有大量使用 Java 时)
更新:我终于对线程与 fork 进行了一些计时我的空闲时间”。 看看:
http://roboprogs.com/devel/2009.04.html
扩展:
http://roboprogs.com/devel/2009.12.html
How about "fork"? (I assume that is what the ForkingMixIn does) If the requests are handled in a "shared nothing" (other than DB or file system) architecture, fork() starts pretty quickly on most *nixes, and you don't have to worry about all the silly bugs and complications from threading.
Threads are a design illness forced on us by OSes with too-heavy-weight processes, IMHO. Cloning a page table with copy-on-write attributes seems a small price, especially if you are running an interpreter anyway.
Sorry I can't be more specific, but I'm more of a Perl-transitioning-to-Ruby programmer (when I'm not slaving over masses of Java at work)
Update: I finally did some timings on thread vs fork in my "spare time". Check it out:
http://roboprogs.com/devel/2009.04.html
Expanded:
http://roboprogs.com/devel/2009.12.html
一种解决方案是 gevent。 Gevent 将基于 libevent 的事件轮询与 greenlet 实现的轻量级协作任务切换结合起来。
您将获得事件系统的所有性能和可扩展性以及优雅且简单的阻塞 IO 编程模型。
(我不知道关于回答真正老问题的SO惯例是什么,但我决定仍然添加我的2美分)
One sollution is gevent. Gevent maries a libevent based event polling with lightweight cooperative task switching implemented by greenlet.
What you get is all the performance and scalability of an event system with the elegance and straightforward model of blocking IO programing.
(I don't know what the SO convention about answering to realy old questions is, but decided I'd still add my 2 cents)
我可以建议额外的链接吗?
cogen 是一个跨平台库,用于使用 Python 2.5 中的增强生成器进行面向网络、基于协程的编程。 在 cogen 项目的主页上有几个具有类似目的的项目的链接。
Can I suggest additional links?
cogen is a crossplatform library for network oriented, coroutine based programming using the enhanced generators from python 2.5. On the main page of cogen project there're links to several projects with similar purpose.
我喜欢道格拉斯的回答,但顺便说一句......
您可以使用集中式调度线程/进程,使用
select
并委托给工作线程池/进程 帮助实现并行性目标。然而,正如 Douglas 提到的,在大多数冗长的 I/O 操作期间,GIL 不会被保留(因为没有发生任何 Python-API 事件),因此,如果您担心的是响应延迟,您可以尝试移动您的代码到 CPython API。
I like Douglas' answer, but as an aside...
You could use a centralized dispatch thread/process that listens for readiness notifications using
select
and delegates to a pool of worker threads/processes to help accomplish your parallelism goals.As Douglas mentioned, however, the GIL won't be held during most lengthy I/O operations (since no Python-API things are happening), so if it's response latency you're concerned about you can try moving the critical portions of your code to CPython API.
http://docs.python.org/library/socketserver.html#asynchronous- mixins
对于多处理器(多核)机器。 对于 CPython,由于 GIL 你至少需要一个每个核心的进程,以扩展。 正如您所说,您需要 CPython,您可能会尝试使用
ForkingMixIn
对其进行基准测试。 Linux 2.6 可能会产生一些有趣的结果。另一种方法是使用 Stackless Python。 这就是EVE 是如何解决这个问题的。 但我明白这并不总是可能的。
http://docs.python.org/library/socketserver.html#asynchronous-mixins
As for multi-processor (multi-core) machines. With CPython due to GIL you'll need at least one process per core, to scale. As you say that you need CPython, you might try to benchmark that with
ForkingMixIn
. With Linux 2.6 might give some interesting results.Other way is to use Stackless Python. That's how EVE solved it. But I understand that it's not always possible.