我正在编写一个简单的守护进程来接收来自N个移动设备的数据。设备将轮询服务器并将其所需的数据作为简单的JSON发送。在通用术语中,服务器将接收数据,然后用它“做东西”。
我知道这个话题已被打过很多次,但我很难理解其优缺点。
就并发性和可伸缩性而言,线程或事件(在Python中认为Twisted)是否更适合这种情况?事件模型似乎更有意义,但我想轮询你们。数据进来 - >过程数据 - >等待更多数据。如果“做东西”是计算密集型的东西怎么办?如果“do stuff”非常密集(例如插入数据库),该怎么办?这会阻止事件循环吗?每种方法的优点和缺点是什么?
答案 0 :(得分:5)
我只能在Python的背景下回答,因为那是我的大部分经历。根据您选择的语言,答案实际上可能略有不同。例如,Python在并行化I / O密集型操作方面比CPU密集型操作要好得多。
扭曲,龙卷风,gevent等异步编程库非常擅长并行处理大量I / O.如果您的工作负载涉及许多客户端连接,执行轻型CPU操作和/或大量I / O操作(如数据库读/写),或者您的客户端正在建立主要用于I / O的长期连接(想想WebSockets),然后一个异步库将非常适合您。大多数Python异步库都有流行数据库的异步驱动程序,因此您可以在不阻塞事件循环的情况下与它们进行交互。
如果您的服务器将要进行大量的CPU密集型工作,您仍然可以使用异步库,但必须了解每次进行CPU工作时,事件循环都将被阻止。没有其他客户能够做任何事情。但是,有办法解决这个问题。您可以使用线程/进程池来管理CPU工作,并且只是异步等待响应。但显然这会使你的实现变得复杂一点。
使用Python,使用线程实际上并不能通过CPU操作为您买单,因为在大多数情况下只有一个线程可以运行一段时间,因此您并没有真正获得拥有多核CPU(google" Python GIL"了解更多信息)。忽略线程中特定于Python的问题,线程将让您避免阻塞事件循环"完全问题,并且线程代码通常比异步代码更容易理解,特别是如果您不熟悉异步编程的工作方式。但是你还必须处理常见的线程问题(同步共享状态等),并且它们不会像许多客户端那样进行扩展以及异步I / O(参见http://en.wikipedia.org/wiki/C10k_problem)
这两种方法在生产中都非常成功地使用,因此您需要更好地决定哪种方法适合您的需求/偏好。
答案 1 :(得分:3)
我认为您的问题属于'依赖'类别。
不同的语言在线程/进程/事件方面有不同的优点和缺点(python在与global interpreter lock绑定的线程中有一些特殊的弱点)
除此之外,当您查看进程与线程与事件时,操作系统也有不同的优缺点。 unix上的内容不会与windows相同。
话虽如此,我整理多方面IO项目的方式是:
这些项目很复杂,没有任何工具可以简单地消除复杂性,因此您有两种选择:
- 让操作系统处理尽可能多的复杂性,使程序员的生活更轻松,但代价是机器效率
- 让程序员具有尽可能多的复杂性,以便他们能够优化设计并尽可能多地挤出机器,代价是需要更高端程序的更复杂的代码。
醇>选项1通常最好通过将任务分解为每个线程/进程一个阻塞状态机的线程或进程来实现
选项2通常最好通过将所有任务复用到一个进程并使用OS挂钩进行事件系统来完成。 (
select
/poll
/epoll
/kqueue
/WaitForMultipleObjects
/CoreFoundation
/ libevent等。)
根据我的经验,项目框架/ internal-arch经常归结为手头的程序员的技能(以及项目对硬件的预算)。
如果你有程序员在OS内部有背景:Twisted对python很有用,Node.js对Javascript很有用,libevent / libev对C或C ++很有用。你最终会得到可以轻松扩展的超高效代码,尽管你会有一个噩梦,试图聘请更多的程序员
如果您有新手程序员,并且可以将资金转入大量云服务,那么将项目分解为多个线程或流程将为您提供最佳工作机会,尽管扩展最终会成为一个问题。
总而言之,我会说多个迭代项目的最佳模式是在简单的阻塞工具(烧瓶)中进行原型设计,然后重新写入更难/更可扩展(扭曲)的东西,否则你会陷入困境经典Premature optimization is the root of all evil陷阱
答案 2 :(得分:0)
连接方案在选择中也很重要。您期望多少并发连接?客户保持联系多长时间?
如果每个连接都绑定到一个线程,那么许多并发连接或非常持久的连接(与websockets一样)将阻塞系统。对于这些场景,基于事件循环的解决方案将更好。
当连接很短并且在断开连接后进行重型处理时,两种型号都会相互称重。