从PHP工人到Python线程
现在我在每台机器上运行着50个PHP工作进程(也就是程序),它们在等待接收工作任务。比如说,处理一张图片的大小。在这个工作任务中,它们会收到图片(以二进制数据的形式)和想要的尺寸。工作进程完成任务后,会把调整好的图片返回,然后继续等待新的工作(它们以一种聪明的方式循环等待)。我在想,我是不是在每个进程中都加载和实例化了相同的可执行文件、库和类50次?这样听起来似乎不太高效。
我现在想要的是一个进程来处理所有的工作,这样可以利用所有可用的CPU核心,同时只加载一次所有的内容(这样更高效)。我想每接到一个工作任务,就会启动一个新的线程,等这个线程完成后就停止。如果正在工作的线程少于50个,就可以接收更多的工作。如果所有50个线程都在忙,就不再接收新的工作了。
我使用了很多库(比如Memcached、Redis、MogileFS等),以便访问系统使用的各种组件,而Python几乎是除了PHP之外,唯一一个支持所有这些库的语言。
Python能做到我想要的效果吗?它会比现在的PHP方案更快、更高效吗?
3 个回答
如果你使用的是一个正常的操作系统,那么共享库应该只加载一次,并且可以被所有使用它的进程共享。虽然数据结构和连接句柄的内存会被复制,但停止和启动系统的开销可能会比让它们在闲置时保持运行要大。如果你在使用像gearman这样的工具,可能会更合理地让几个工作进程保持运行,即使它们没有在忙碌。然后可以有一个持续监控的进程,如果所有当前的工作进程都忙碌到达某个阈值,比如可用CPU的数量,它就会启动新的工作进程。这个监控进程还可以在工作进程闲置一段时间后,以后进先出的方式结束它们。
Linux有共享库,所以那50个php进程大部分都在使用相同的库。听起来你根本没有什么问题。
“这听起来不是很有效。”这并不是一个问题的描述,反而这句话本身就是个问题。写代码需要有真正的理由,否则你就是在浪费时间和/或金钱。
Python是一种很不错的语言,性能不会比php差。Python的multiprocessing模块可能也会大有帮助。但是如果php的实现没有什么特别糟糕的地方,那其实没什么好处。所以当一切都正常运作时,为什么还要花时间去改呢?这通常是我们的目标,而不是重写的理由……
很可能是的。不过不要急着认为你必须使用多线程。可以看看 multiprocessing 模块。这个模块里已经包含了一个叫做 Pool 的实现,你可以用它。简单来说,它解决了 GIL 问题(多线程在任何时候只能运行一个“标准的 Python 代码”——这是一个非常简单的解释)。
它会为每个任务分叉一个进程,但和重新启动整个进程的方式不同。所有在进入工作进程之前完成的初始化和加载的库都会以一种“写时复制”的方式被继承。这样,你就不会做多余的初始化,也不会浪费内存在同一个库或类上,除非你真的把它和池子里的状态搞得不一样。
所以,从这一点来看,Python 会更有效地利用资源,并且会使用一个“更友好”的工作池模型。至于它是否真的会更快或者对 CPU 的消耗更少,这个很难说,除非你自己测试一下,或者至少看看代码。你可以自己试试。
补充:如果你担心内存使用,Python 可能会帮你一点,因为它有一个“合适”的垃圾回收机制,而在 PHP 中,垃圾回收并不是优先考虑的事情,效果也不是很好(这也是有原因的)。