确保Rails和Sidekiq不会超过最大数据库连接

时间:2016-09-09 17:17:52

标签: ruby-on-rails passenger sidekiq

我正在使用带有单线程Rails应用程序的Phusion Passenger的Nginx。这是捕获。在该应用程序中,我使用多线程sidekiq来执行一些后台作业。通常在我的database.yml中,我只需要将池值设置为1.这是一个例子:

default: &default
  adapter: mysql2
  encoding: utf8
  collation: utf8_unicode_ci
  pool: 1
  username: username
  password: password
  host: localhost

原因是因为每个tcp套接字连接打开,当http请求通过该套接字进入时,nginx将接收请求并将信息传递给乘客。 Passenger检测到它的Rails应用程序,它产生一个Rails实例,它将响应转换为html,然后发送回nginx,然后传回给客户端(浏览器)。因此对于每个乘客实例,我只需要一个数据库连接,使用单线程Rails应用程序。

但在我的sidekiq.yml中,我将并发设置为5:

:concurrency: 5 

这意味着对于每个乘客机架实例,我将有由sidekiq处理的5个并发线程加上主应用程序的一个连接,即一个乘客实例的总共6个数据库连接。

当我查看乘客状态时,我注意到max_pool_size设置为6:

----------- General information -----------
Max pool size : 6

这是否意味着乘客不会同时产生超过6个Rails实例?如果是这样的话,这是否意味着我的数学是正确的:6(实例)* 6(数据库连接:5为sidekiq,1为主app)= 36(我的rails应用程序可以同时处理的总数据库连接) )。

现在我的mysql数据库配置为处理151个最大并发连接。

SHOW VARIABLES LIKE "max_connections";
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+

我只是想确保我的数学运算正确,乘客,铁路和sidekiq。

2 个答案:

答案 0 :(得分:2)

首先,您的Sidekiq流程和您的Web服务器(在您的情况下,Passenger)是分开的。 Passenger的线程池大小对您的Sidekiq并发性没有影响;相反,您的Sidekiq配置指定单独的并发。所以,我们将分别考虑这两个:

乘客

ActiveRecord数据库池值是您的Web进程将使用的数据库连接数,在所有线程中 。如果您的Passenger服务器设置为多进程模式,那么您的Web进程的最大连接数为db pool size * passenger pool size。另一方面,如果你在多线程模式下设置它(如果可能的话我推荐),你的最大连接只是db pool size(但是乘以许多进程正在运行;例如,Puma运行默认情况下,两个进程最多包含15个线程,因此在这种情况下最大连接数为30)。

因此,如果你使用的是多线程模式,那么池大小1是绝对不够的 - 你至少需要像预期的那样拥有一个大的池。在多进程模式下,1 可能会工作,但我怀疑它是否真的值得默认5,直到遇到问题。

Sidekiq

Sidekiq总是以多线程模式运行(你可以在技术上运行多个进程,但我会假设你没有)。因此,如上所述,您希望连接池至少与线程数一样大。这可能意味着您在技术上需要两个不同的db池值值,具体取决于Rails env是为Passenger启动还是为Sidekiq启动 - 有关如何使用的更多信息,请参阅this issue on the Sidekiq repothis helpful Heroku guide解决这个问题。

总结

不要忘记,除了上述所有内容之外,您可以轻松地让多个服务器都运行相同的Rails应用程序,但只有一个数据库具有一个连接限制。如果您在多实例模式下运行Passenger,最多有6个进程,请将数据库池大小设置为5,然后每个Web服务器节点最多使用30个连接。如果它正在运行Sidekiq服务器,那么添加5。您可能不需要多个Sidekiq服务器,因此4个Web节点@30个连接+一个Sidekiq进程@ 5个连接= 125个最大连接,完全在您的MySQL连接限制内。

答案 1 :(得分:1)

我再次回顾了Passenger文档,虽然上面的答案回答了这个问题,但我想补充一点细节:

  • HTTP客户端通过TCP向Nginx发送请求
  • Phusion Passenger加载到Nginx检查请求是否应由Passenger处理。如果是,请求将发送到Passenger Core。
  • 使用负载均衡规则的Passenger核心确定应将请求转发到哪个进程。
  • Passenger核心还负责应用程序生成:如果它确定有更多的应用程序进程是必要的或有益的,那么它将根据用户配置的限制实现:核心永远不会产生比用户配置更多的进程最大。
  • 乘客核心也有监控和统计:乘客记忆统计和乘客状态
  • 如果应用程序进程崩溃,则会重新启动应用程序进程。
  • UstRouter处于空闲状态,如果您没有将其配置为将数据发送到联合工作站(监控Web服务),则不会消耗资源
  • Watchdog监控Passenger Core和UstRouter。如果它们中的任何一个崩溃,它们将由Watchdog重新启动。

    passenger-memory-stats将验证上述三个流程以及衍生的机架应用程序:

------ Passenger processes ------

PID    VMSize     Private   Name
---------------------------------
18355  419.1 MB   ?         Passenger watchdog
18358  1096.5 MB  ?         Passenger core
18363  427.2 MB   ?         Passenger ust-router
18700  818.9 MB   256.2 MB  Passenger RubyApp: myapp_rack_rails
24783  686.9 MB   180.2 MB  Passenger RubyApp: myapp_rack_rails

乘客状态显示max_pool_size为6.也就是说,乘客核心最多会有6个机架应用程序:

----------- General information -----------
Max pool size : 6
App groups    : 2
Processes     : 3

如另一个答案所述,ActiveRecord数据库池值是您的Web进程将在所有线程中使用的数据库连接数。

但是由于我使用的是在多进程模式下设置的免费Passenger服务器,因此我的Web进程的最大连接数是db pool size * passenger pool size。因此,由于乘客池大小为6,如果我的数据库池大小为1,则为6 * 1 = 6.这将是6个最大数据库连接。

Sidekiq总是以多线程模式运行。

如果有人想使用sidekiq,他们必须配置他们想要运行的线程数或使用默认值(25)。如果他们正在使用数据库(可能)然后没有遇到连接超时错误,他们将需要在其数据库池中至少具有与sidekiq线程一样多的连接。目前,他们必须在两个不同的位置配置这两个值,数据库池在database.yml中用于ActiveRecord,而sidekiq连接可以通过命令行或sidekiq yml文件。这是一个问题,因为当您修改一个需要修改它们的值时很难记住。