PHP Workers 转为 Python 线程

61 阅读4分钟

目前,我在每台机器上运行 50 个 PHP(CLI 模式)单独的 worker(进程),等待接收工作负载(作业),例如,调整图像大小的作业。在工作负载中,它们会接收图像(二进制数据)和所需的大小。worker 完成工作并返回调整大小后的图像。然后它等待更多作业(以一种智能的方式循环)。我假设我加载并实例化了相同的可执行文件、库和类 50 次。我说的正确吗?因为这听起来不是很有用。

现在我想要的是一个进程来处理所有这些工作,并能够在一切只加载一次的情况下使用所有可用的 CPU 内核(提高效率)。我认为将为每个作业启动一个新线程,并且在作业完成后,该线程将停止。如果正在执行工作的线程少于 50 个,则将接受更多作业。如果所有 50 个线程都处于繁忙状态,则不会接受其他作业。

我正在使用许多库(用于 Memcached、Redis、MogileFS 等)来访问系统使用的各种组件,并且 Python 是除 PHP 之外几乎唯一支持所有这些库的语言。

Python 能做到我想要的吗?它会比当前的 PHP 解决方案更快、更高效吗?

2、解决方案

方法一:保持现状

Linux具有共享库,因此这50个php进程主要使用相同的库。您甚至没有问题。 “这听起来效率不高。”本身就是一个问题描述,如果没有,这些话本身就是问题。编写代码需要一个真正的理由,否则您只是在浪费时间和/或金钱。Python是一种不错的语言,并且不会比php表现更差。Python的多处理模块可能也会有很大帮助。但如果php实现不是完全疯狂的,那么就没有什么可获得的。那么为什么要花时间去关注它呢?这通常是目标,而不是重写的原因...

方法二:使用Python的多处理模块

很可能 - 是的。但是不要认为您必须进行多线程处理。请查看多处理模块。它已经包括了一个Pool的实现,这是您可以使用的东西。并且它基本解决了 GIL 问题(多线程一次只能运行 1 个“标准 python 代码” - 这是一个非常简单的解释)。

它仍然会为每个作业派生出一个进程,但方式不同于从头开始。在进入工作进程之前完成的所有初始化和加载的库都将以写时复制的方式继承。您不会进行比必要更多的初始化,并且如果您没有実際にそれをプール前の状態と違わせた場合,您不会为相同的库/类浪费内存。

因此 - 仅从这一部分来看,python 将浪费更少的资源,并将使用“更好”的工作器池模型。它是否真的会更快/更少地滥用 CPU,如果不进行测试或至少查看代码,很难说。自己试试看。

添加:如果您担心内存使用,python 也可能对您有所帮助,因为它具有“适当的”垃圾收集器,而在 php 中,GC 不是优先级也不是很好(并且也有很好的理由)。

方法三:使用 gearman

如果您使用的是合理的操作系统,那么共享库应该只加载一次,并在使用它们的进程之间共享。用于数据结构和连接句柄的内存显然会重复,但是停止和启动系统的开销可能大于在空闲时保持系统正常运行的开销。如果您使用的是类似 gearman 的东西,那么让几个工作进程保持运行(即使空闲)可能是有意义的,然后有一个持续的监视进程,如果所有当前工作进程都忙于达到阈值(例如可用 CPU 的数量),则该进程将启动新的工作进程。然后,该进程可以按照 LIFO(后进先出)方式在工作进程空闲一段时间后杀死它们。