Nei*_*eil 9 python process nginx flask gunicorn
我想构建一个能够处理的服务:
我对 pre-fork 服务器的理解是会发生以下类似的事情:
我想了解的是,如果在我的 Flask 代码中处理请求时,会发生什么:
from multiprocessing import pool as ProcessPool
with ProcessPool(4) as pool:
pool.map(some_expensive_function, some_data)
Run Code Online (Sandbox Code Playgroud)
特别是:
Wil*_*lva 27
好问题!对于 Python 多处理,可以使用 3 种“启动方法”,它们都对您的问题有影响。正如文档所解释的,它们是:
\n\n\n\n
\n- \n
\'spawn\':父进程启动一个新的Python解释器进程。子进程只会继承运行进程对象\xe2\x80\x99srun()方法所需的资源。特别是,来自父进程的不必要的文件描述符和句柄将不会被继承。与使用 fork 或 forkserver 相比,使用此方法启动进程相当慢。可在 Unix 和 Windows 上使用。Windows 和 macOS 上的默认设置。- \n
\'fork\':父进程用于os.fork()派生Python解释器。子进程在启动时实际上与父进程相同。父进程的所有资源都被子进程继承。请注意,安全分叉多线程进程是有问题的。仅适用于 Unix。Unix 上的默认值。- \n
\'forkserver\'当程序启动并选择forkserver启动方式时,就会启动一个服务器进程。从那时起,每当需要新进程时,父进程就会连接到服务器并请求它派生一个新进程。Fork 服务器进程是单线程的,因此使用起来是安全的os.fork()。不会继承不必要的资源。可用于支持通过 Unix 管道传递文件描述符的 Unix 平台。
至于Gunicorn的预分叉模型,你已经解释得很好了。每个工人都在自己的进程中运行。由于您尝试在工作线程中使用多处理,而不是与 Gunicorn 一起使用,所以这应该是可行的,但仍然容易出错。
\nimport multiprocessing\n\nmp = multiprocessing.get_context(\'spawn\')\nRun Code Online (Sandbox Code Playgroud)\n此代码为我们提供了mp对象,该对象具有与多处理模块相同的 API,但具有设置启动方法。在上面的代码中,它被设置为\'spawn\'。这是在 Gunicorn 工作线程中使用多处理的最安全途径,因为它与创建它的进程最隔离,并且不太可能遇到意外共享资源的问题。
with mp.Pool(processes=4) as pool:\n pool.map(some_expensive_function, some_data)\nRun Code Online (Sandbox Code Playgroud)\n然后,我们mp像您一样使用该对象创建进程池。此代码必须位于仅在工作进程中调用/使用的函数/模块内。如果在服务器进程中使用它可能会导致问题。
\n\n\n
\n- 是否会启动其他操作系统进程?加速效果会达到我的预期吗?(即,类似于我在
\nProcessPoolFlask 生产环境之外运行?)
这里包含了相当多的问题。将启动其他操作系统进程。加速可能会有很大差异,并且取决于许多因素,例如:
\nsome_expensive_function(data_1)必须等待some_expensive_function(data_2)才能完成其工作?要确定使用多重处理是否更快以及快多少,您必须对其进行测试。在此之前您可以做的最好的事情就是根据上面列出的因素进行粗略估计。
\n\n\n\n
\n- (续)如果 Gunicorn 创建了 4 个 Web Worker,那么现在会有 7 个操作系统进程在运行吗?9?制作过多是否存在风险?Gunicorn 是否假设每个工人都不会分叉或者不在乎?
\n
如果有 4 个 Gunicorn 工作进程,并且每个进程都在执行一个使用 4 个进程的多处理的请求,那么将有 1 个 Gunicorn 父进程 + 4 个工作进程 + 4 * 4 个工作子进程 = 21 个进程,更不用说进程了正在被 Nginx 使用。
\nGunicorn 建议您创建(2 * num_cores) + 1工作线程,但在您的情况下,您可能希望减少该值(也许将其除以 4),以考虑到您的工作线程进程在使用多核时自身工作效果最佳这一事实。要找到最有效的配置,您必须对各种配置进行基准测试,以找出最适合您的配置。
\n\n\n
\n- 如果 Web Worker 在启动 ProcessPool 后死亡或被杀死,上下文管理器是否会正确关闭它?
\n
这取决于工人如何死亡。如果它通过 SIGKILL 被杀死,或者遇到分段错误或其他一些严重错误,那么它会突然死亡,而不会运行任何最终代码。上下文管理器只能在 try-finally 块能够执行“finally”块的情况下完成其工作。有关更多信息,请查看此答案:Does 'final' alwaysexecute in Python?
\n\n\n\n
\n- 这是明智之举吗?有哪些替代方案?
\n
这本身并不疯狂,但这不是我通常推荐的方法。一种替代方案是some_expensive_function使用自己的服务器来实现。您的 Gunicorn 工作人员可以使用 IPC 或网络通信将工作发送到some_expensive_function服务器进程,并且它将处理在子进程之间划分此工作。此类设计的一个优点是,some_expensive_function如果性能需要,可以轻松地将服务器进程转移到另一台计算机上运行。
它类似于数据库通常作为自己的服务器进程运行的方式,并且可以位于同一台计算机上或位于单独的计算机上(可能位于用于只读查询的负载平衡器或分片配置后面),具体取决于必须满足哪些性能要求。
\n如果您决定走这条路,您可能会发现 Python 包Celery对于分发 Gunicorn 工作人员的工作非常有用。
\n如果您想这样做,您可能应该使用preload_app=True.
| 归档时间: |
|
| 查看次数: |
416 次 |
| 最近记录: |