Puma中的Workers和Threads有啥区别
Posted
技术标签:
【中文标题】Puma中的Workers和Threads有啥区别【英文标题】:What is the difference between Workers and Threads in PumaPuma中的Workers和Threads有什么区别 【发布时间】:2014-08-08 11:09:57 【问题描述】:在 heroku dyno 的上下文中,puma worker 和 puma thread 有什么区别?
我知道的(如果我错了,请纠正我):
Thin 不是并发的,所以一个 Web 进程一次只能做一个请求
在 unicorn 中,我知道我可以在一个进程中拥有多个 unicorn worker 以增加并发性。
但是在 puma 中,有线程和工人。。工人不是 puma 进程中的线程吗?
我可以在 Heroku 中使用更多的工作线程/线程来添加 Web 并发吗?
【问题讨论】:
【参考方案1】:正如另一个答案所说,这个Heroku article 对某些配置项的解释非常好。
但是,如果您需要在 Heroku 或任何地方调整您的应用程序,那么了解其工作原理是值得的。
当您说“工作人员是 puma 进程中的线程”时,我认为您几乎是正确的,我相信工作人员是从 puma 派生的操作系统级进程,然后可以在内部使用线程。
据我了解-puma 将分叉其操作系统进程,无论您通过workers
配置设置多少次来响应http 请求。这在处理多个请求方面为您提供了并行性,但这通常会占用更多内存,因为它会为每个工作人员“复制”您的应用程序代码。
然后每个 puma worker 将在其操作系统进程中使用多个线程,具体取决于threads
配置。这些通过允许 puma 进程自己响应多个请求来增加并发性,这样如果一个线程被阻塞,即处理一个请求,它可以处理另一个线程的新请求。如前所述,这要求您的整个应用程序是线程安全的,例如,来自一个请求的任何全局配置都不会“泄漏”到另一个请求中。
您将调整 puma 以使工作人员的数量足以满足可用 CPU 和内存的数量,然后根据您希望运行应用程序的主机饱和的程度以及应用程序的行为方式来调整线程 - 更多并不总是等于更快/更多的请求吞吐量。
【讨论】:
【参考方案2】:这是一个很大的领域,但是我不是专家......
Puma 可以产生许多工作人员,每个工作人员可以使用多个线程来处理请求。
据我所知,Unicorn 没有线程,它只有 worker 模型。
如果您使用线程,则需要确保您的代码是线程安全的。这意味着 Rails、您依赖的任何 gem 以及您自己的代码。
为了获得最佳性能,您可能还想查看具有适当线程支持的JRuby 或Rubinius。 MRI 受其 GIL 限制。
有一个good article on Heroku 解释了 Puma 如何使用工人和线程。您可能应该阅读并忽略我:)
【讨论】:
“...每个工作人员可以使用多个线程来处理请求”是不正确的。每个请求它只会使用一个线程。 (除非您的应用代码手动生成线程)。如果可以使用多个线程在同一进程中处理多个请求。【参考方案3】:我只想强调这里引用的 Heroku/Puma 文章中最重要的一行:
Rails 维护自己的数据库连接池,带有一个新的池 为每个工作进程创建。工作线程中的线程将运行 在同一个池中。
它声明每个 Worker 将有自己的池。然而:
worker 中的线程将在同一个池上运行。
理解这一点非常重要。如果 Puma Worker 每个 Worker 使用 5 个线程,那么 database.yml 必须配置为 5 个连接池,因为每个线程都可能建立数据库连接。
由于每个 Worker 都是由系统 fork() 生成的,新的 Worker 将拥有自己的一组 5 个线程来使用,因此对于创建的新 Rails 实例,database.yml 仍将设置为连接5 个。
现在 database.yml 连接池和你的实际数据库池是两个不同的东西。与数据库的 TOTAL 连接需要使用 Heroku 文档提到的特定公式:
确定每个应用程序所需的连接数的一个很好的公式是将 RAILS_MAX_THREADS 乘以 WEB_CONCURRENCY。
这意味着如果您使用 2 个 Worker,每个 5 个线程,则 2 * 5 = 10,因此您的数据库必须配置为接受 10 个并发连接。
【讨论】:
【参考方案4】:尝试从线程理解之旅开始的人的角度回答问题:
-
worker 是该 Rails 应用程序的新操作系统实例,具有应用程序的所有代码和 ENV 变量,并具有自己的独立 RAM。
可以将一个工作线程配置为具有多个线程。重要的是要知道,这些线程中的每一个都将与该特定工作线程的其他线程共享相同的 RAM 和 ENV 变量状态。
worker 一次只能在一个线程上工作,因此当当前线程卡在进行某些处理时,该worker 将开始在新线程上工作。
鉴于变量的状态在所有线程之间共享,这可能会导致第一个线程更改了一个可变数据结构,而第二个线程需要处于初始状态。
使您的代码线程安全通常很复杂。除了其他答案中提出的解决方案之外,我还要说查看Mutex 并了解您的数据库处理线程的方式是个好主意。为此,我推荐this Postgresql SO 回答。
【讨论】:
以上是关于Puma中的Workers和Threads有啥区别的主要内容,如果未能解决你的问题,请参考以下文章
谁在运行Puma或Passenger时设置RAILS_MAX_THREADS环境变量?
Tasks, Workers, Threads, Scheduler, Sessions, Connections, Requests – what does it all mean?