【问题标题】:Ensure max database connections is not exceeded with Rails and Sidekiq确保 Rails 和 Sidekiq 不超过最大数据库连接数
【发布时间】:2016-09-09 17:17:52
【问题描述】:

我正在将 Nginx 与 Phusion Passenger 与单线程 Rails 应用程序一起使用。这就是问题所在。在该应用程序中,我使用多线程 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 会接受该请求并将信息传递给乘客。乘客检测到它是一个 Rails 应用程序,它会生成一个 Rails 实例,它将响应转换为 html,将其发送回 nginx,然后将其传递回客户端(浏览器)所以对于每个乘客实例,我只需要一个数据库连接,带有单线程 Rails 应用程序。

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

:concurrency: 5 

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

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

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

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

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

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

我只是想确保我的关于乘客、轨道和 sidekiq 的数学是正确的。

【问题讨论】:

    标签: ruby-on-rails passenger sidekiq


    【解决方案1】:

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

    乘客

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

    因此,如果您使用多线程模式,1 的池大小绝对不够——您至少需要一个与您期望的线程一样大的池。在多进程模式下,1可能可以工作,但我怀疑在遇到问题之前,是否真的值得偏离默认的5

    Sidekiq

    Sidekiq 始终以多线程模式运行(从技术上讲,您也可以运行多个进程,但我假设您不是)。因此,就像上面一样,您希望连接池至少与线程数一样大。这可能意味着从技术上讲,您的数据库池值需要两个不同的值,具体取决于 Rails 环境是为Passenger 还是为Sidekiq 旋转——有关如何解决这个问题的更多信息,请参阅this issue on the Sidekiq repothis helpful Heroku guide

    总结

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

    【讨论】:

    • “网络服务器节点”是什么意思?我尝试了谷歌搜索并得到了不同的解释。
    • @Donato:“节点”是指服务器。随着生产 Rails 应用程序的扩展,您通常会发现最好将集群中的每个附加服务器专用于特定目的。例如,web,这意味着它运行您的 rails server 命令,即乘客;或worker,用于您的 Sidekiq 服务器。这只是惯例,但我认为您会发现它是一种非常常见的模式。
    • 实际上,如果您描述了您的(预期或当前)生产环境,可能会有所帮助。
    • 还要注意AR的连接池是惰性的,所以你可以设置pool: 1000,它只会为每个进程创建尽可能多的连接。
    【解决方案2】:

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

    • HTTP 客户端通过 TCP 向 Nginx 发送请求

    • 加载到 Nginx 中的 Phusion 乘客检查请求是否应由乘客处理。如果是,则将请求发送到Passenger Core。

    • Passenger core 使用负载平衡规则确定应将请求转发到哪个进程。

    • 乘客核心还负责应用程序生成:如果它确定拥有更多应用程序进程是必要的或有益的,那么它将根据用户配置的限制来实现:核心永远不会生成比用户配置更多的进程最大。

    • Passenger核心也有监控和统计:passenger-memory-stats和passenger-status

    • 如果应用程序进程崩溃,乘客核心会重新启动它。

    • UstRouter 处于空闲状态,如果您未将其配置为向 Union Station(一种监控 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
    

    passenger-status 显示 max_pool_size 为 6。也就是说,Passenger Core 最多会产生 6 个机架应用:

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

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

    但由于我使用的是免费的乘客服务器,它设置为多进程模式,所以我的网络进程的最大连接数是数据库池大小 * 乘客池大小。因此,由于乘客池大小为 6,如果我的数据库池大小为 1,则为 6 * 1 = 6。这将是 6 个最大数据库连接。

    Sidekiq 始终以多线程模式运行。

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

    【讨论】:

      猜你喜欢
      • 2020-02-14
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-05
      相关资源
      最近更新 更多