RAILS_MAX_THREADS 是由 Puma 在构建时设置和调整的,还是我需要自己设置?

18

我知道Rails 5使用Puma(我们正在使用)并会查找RAILS_MAX_THREADS作为环境变量或默认为5个线程,但我使用默认值时收到了超时错误。我查看了我的数据库并发现它的最大连接数是几千个。

这可能很傻,但这是Puma会自动设置和扩展的内容吗?还是我需要在环境变量中明确设置?如果需要手动设置,RAILS_MAX_THREADS的一个好值是多少?

我找到了以下有用的资料,但我没有完全掌握可伸缩性部分:

https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server https://devcenter.heroku.com/articles/concurrency-and-database-connections


你遇到了什么样的超时错误?发生在开发阶段还是生产环境中? - Nikolay Shebanov
感谢@NickShebanov,这种情况发生在我们的生产环境中,当我们有高峰流量时。我们已经将这个值增加到25,但在高峰期仍然偶尔会出现这种情况。我们通过根据需要启动新服务器来缓解这种情况。我现在知道RAILS_MAX_THREADS不会自动扩展,但将其设置为~100是否荒谬? - Michael
1个回答

49

Puma其实有两个参数,分别是线程数和worker数。如果我们稍微更改默认的puma.rb,它会变成这样:

Puma实际上有两个参数,分别为线程数和工作进程数。如果我们轻微修改默认的puma.rb文件,它就会变成这样:

# WORKERS_NUM is not a default env variable name
workers Integer(ENV['WORKERS_NUM'] || 1)
max_threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 1)
min_threads_count = max_threads_count
threads min_threads_count, max_threads_count

工作者数量是Puma为您生成的单独进程数。通常,将其设置为服务器上的处理器核心数是一个好主意。您可以生成更多的工作者以允许同时处理更多请求,但工作者会创建额外的内存开销-每个工作者都会旋转您的rails应用程序的副本,因此通常使用线程来实现更高的吞吐量。

RAILS_MAX_THREADS是一种在幕后设置每个工作者将使用的线程数的方法。在上面的示例中,min_threads_count等于max_threads_count,因此线程数是恒定的。如果将它们设置为不同,则会从最小值扩展到最大值,但我没见过这种情况。

有几个限制线程数量的原因-解释器和响应时间:

  1. 如果你使用MRI,你的线程受GIL限制,因此它们不会并行运行。MRI通过上下文切换模拟并行执行。大量的线程将允许更多的同时连接,但由于GIL,平均响应时间将增加。
  2. 平台限制:例如heroku有线程数限制https://devcenter.heroku.com/articles/dynos#process-thread-limits,linux仅限制进程数Linux下每个进程的最大线程数?
  3. 当代码不是线程安全时,使用多个线程可能会导致不可预知的问题。这实际上就是我的情况,所以我没有尝试过线程数量。

还有一个论点是,缓慢的IO会阻止ruby进程并且不允许上下文切换(例如调用外部服务或即时生成大文件),但事实证明这不正确http://yehudakatz.com/2010/08/14/threads-in-ruby-enough-already/。但是,尽可能在后台完成尽可能多的工作,总是一个好主意。

此答案将帮助您找到给定硬件的线程数与工作者数的完美组合。

这个展示了如何进行基准测试以找到确切的数字。

总之:WORKERS_NUM乘以RAILS_MAX_THREADS给出Puma可以处理的最大并发连接数。如果该数字太低,您的用户在负载高峰期间将看到超时错误消息。为了在使用MRI的情况下实现最佳性能,您需要将WORKERS_NUM设置为核心数量,并根据性能测试期间的平均响应时间找到最佳的RAILS_MAX_THREADS


1
毫无疑问,你是一个天赐之人。 - Michael
@Michael 不用谢,但请记住这是一个非常有偏见的答案,如果你深入挖掘,一些细节可能会与我所说的不同。我只是试图适应你的特定情况。此外,我犯了几个错误(已修正):1.上下文切换增加平均响应时间,而不是减少,当然,2.阻塞IO仍然允许切换线程。 - Nikolay Shebanov
2
我认为每个工作线程都会得到一个新的连接池,所以如果你有两个拥有五个线程的工作者,那么你只需要5个连接池(而不是10个)。如果我错了,希望有人可以纠正我。 - Sean
@Sean 是的,没错。上面的文本有什么问题吗? - Nikolay Shebanov
我刚刚读到你最后一段话,其中提到“workers_num乘以rails_max_threads可以得出puma可以处理的最大并发连接数。”虽然这是正确的,但我可以理解为什么有些人会将其视为计算连接池大小的方法,实际上你应该使用“RAILS_MAX_THREADS”作为连接池大小。 - Sean
“你的用户在负载高峰期间会看到超时”这句话中,超时是由哪个部分引起的?是正常的408还是504? - mecampbellsoup

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