Puma中的Workers和Threads有什么区别?

79

在Heroku dyno的上下文中,puma worker和puma thread有什么区别?

据我所知(如果我错了请纠正):

  • Thin不支持并发,因此Web进程一次只能处理一个请求

  • 在unicorn中,我知道可以在一个进程中运行多个unicorn workers以添加并发性。

但是在puma中有线程和工作进程..难道工作进程不就是puma进程中的线程吗?

我能否使用更多的工作进程/线程来增加Heroku中的Web并发性?

5个回答

79

就像其他答案所述,这篇Heroku文章在解释某些配置项方面做得非常好。

但是,如果您需要在Heroku或任何地方调整应用程序,那么了解事情的工作原理是很有价值的。

当您说“工人是puma进程内的线程”时,我认为您几乎是正确的,我相信工人是从puma分叉出的操作系统级别进程,然后可以在内部使用线程。

据我所知 - puma将分叉其操作系统进程多少次取决于通过workers配置设置以响应http请求。这使您可以处理多个请求并行处理,但这通常会占用更多内存,因为它将为每个工作者“复制”您的应用程序代码。

每个puma工作进程都将根据threads配置在其操作系统进程中使用多个线程。这通过允许puma进程本身响应多个请求来添加并发性,以便如果一个线程被阻塞,即处理一个请求,它可以使用另一个线程处理新的请求。正如所述,这要求您的整个应用程序都是线程安全的,以便例如任何一个请求的全局配置都不会“泄漏”到另一个请求中。

您可以调整puma,使工作者的数量适合可用的CPU和内存数量,然后根据您想要饱和运行应用程序的主机以及应用程序的行为来调整线程数量 - 更多并不总是意味着更快/更多的请求吞吐量。


17

这是一个广泛的领域,我不是专家,但是...

Puma可以生成许多工作进程,每个工作进程可以使用多个线程来处理请求。

据我所知,Unicorn没有线程,它只有工作模型。

如果您使用线程,则需要确保您的代码是线程安全的。这意味着Rails,您依赖的任何gem和您自己的代码都需要考虑。

为了获得最佳性能,您还可能想查看具有适当的线程支持的JRubyRubinius。MRI受其GIL的限制。

Heroku上有一篇好文章,解释了Puma如何使用工作进程和线程。您应该阅读该文章并忽略我 :)


1
“...每个工作进程可以使用多个线程来处理请求”是不正确的。它只会为每个请求使用一个线程(除非您的应用程序代码手动生成线程)。但是,如果可以在同一进程中使用多个线程为多个请求提供服务。 - Jack Casey

13
我只想强调Heroku / Puma文章中最重要的一句话:“Rails维护其自己的数据库连接池,为每个工作进程创建一个新的连接池。工作进程内的线程将在同一连接池上操作。” 每个Worker都有自己的pool, 但是“工作进程内的线程将在同一连接池上操作”非常重要,因此如果Puma Worker使用5个线程,则database.yml必须配置为连接池大小为5,因为每个线程都可能建立一个数据库连接。
由于每个Worker是通过系统fork()生成的,所以新的Worker将具有自己的5个线程,因此对于新创建的Rails实例,database.yml仍将设置为连接池大小为5。
现在,database.yml连接池和实际数据库池是两个不同的事情。需要使用Heroku文档提到的特定公式来确定每个应用程序需要的连接数量:
“确定每个应用程序所需连接数的好公式是将RAILS_MAX_THREADS乘以WEB_CONCURRENCY。”这意味着,如果您使用了2个拥有5个线程的工作者,那么2 * 5 = 10,因此您的数据库必须配置为接受10个并发连接。

1
尝试从一个刚开始了解线程的人的角度回答这个问题:
  1. 工作者是Rails应用程序的新OS实例,具有所有应用程序代码和ENV变量,并具有自己独立的RAM。
  2. 可以配置工作者拥有多个线程。重要的是要知道,每个线程都将与该特定工作者的其他线程共享相同的RAM和ENV变量状态。
  3. 工作者一次只能处理一个线程,因此当当前线程卡在某些处理上时,该工作者将开始处理新线程。
  4. 由于变量状态在所有线程之间共享,这可能会导致第一个线程更改可变数据结构,而第二个线程需要处于初始状态。

使您的代码线程安全通常很复杂。除了其他答案中提出的解决方案外,我还建议查看Mutex并了解DB如何处理线程的方式。为此,我推荐这个Postgresql SO答案。


-1

工作进程:

  • 工作进程是 Puma 服务器的一个独立实例,它在自己的进程中运行。每个工作进程都能够使用多个线程并行处理多个请求。例如,当有新的请求到达时,Puma 主进程会将请求分配给其中一个工作进程,然后该工作进程使用其中一个线程处理请求。

线程:

  • 线程是工作进程内的轻量级执行单元。每个工作进程可以有多个线程,但每个线程只能处理一个请求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接