【问题标题】:How can I prevent database connections from timing out in Rails?如何防止 Rails 中的数据库连接超时?
【发布时间】:2015-02-15 00:18:31
【问题描述】:

我有一个 Rails 系统,其中每半小时完成一次:

  • 网络上其他地方有 15 个客户端
  • 服务器为每个客户端创建一个名为 Measurement 的记录
  • 测量记录被配置,然后通过Sidekiq异步运行,使用MeasurementWorker.perform_async(m.id)
  • 与客户端的连接是通过赛璐珞演员和 WebSocket 客户端完成的
  • 每次测量在运行时都会创建一些存储在数据库中的事件记录

系统在 5 个客户端上运行良好,但现在我 15 岁,当我同时启动它们时,许多测量不再运行,出现以下错误:

2015-02-04T07:30:10.410Z 35519 TID-owd4683iw MeasurementWorker JID-15f6b396ae9e3e3cb2ee3f66 INFO: fail: 5.001 sec
2015-02-04T07:30:10.412Z 35519 TID-owd4683iw WARN: {"retry"=>false, "queue"=>"default", "backtrace"=>true, "class"=>"MeasurementWorker", "ar
gs"=>[6504], "jid"=>"15f6b396ae9e3e3cb2ee3f66", "enqueued_at"=>1423035005.4078047}
2015-02-04T07:30:10.412Z 35519 TID-owd4683iw WARN: could not obtain a database connection within 5.000 seconds (waited 5.000 seconds)
2015-02-04T07:30:10.412Z 35519 TID-owd4683iw WARN: /home/webtv/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems/activerecord-4.1.4/lib/active_
record/connection_adapters/abstract/connection_pool.rb:190:in `block in wait_poll'
....

现在,我的生产环境是这样的:

config/sidekiq.yml

production:
  :verbose: false
  :logfile: ./log/sidekiq.log
  :poll_interval: 5
  :concurrency: 50

config/unicorn.rb

...
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 60
...

config/database.yml

production:
  adapter: postgresql
  database: ***
  username: ***
  password: ***
  host: 127.0.0.1
  pool: 50

postgresql.conf

max_connections = 100 # default

如您所见,我已将 Sidekiq 的并发数增加到 50,以满足大量可能的并发测量。我已将数据库池设置为 50,这对我来说已经有点过分了。

我应该补充一点,服务器本身非常强大,配备 8 GB RAM 和四核 Xeon E5-2403 1.8 GHz。

理想情况下这些值应该设置为多少?我可以使用什么公式来计算它们? (例如最大数据库连接数 = Unicorn workers × Sidekiq 并发数 × N

【问题讨论】:

    标签: ruby-on-rails postgresql sidekiq


    【解决方案1】:

    在我看来,您的 100 池配置没有生效。每个进程最多需要 50 个,因此将 100 更改为 50。我不知道您是否使用 Heroku,但配置池大小非常困难。

    在 mysql 中,您的最大连接数应如下所示:

    ((Unicorn processes) * 1) + ((sidekiq processes) * 50)
    

    Unicorn 是单线程的,不需要多个连接,除非您出于某种原因在 Rails 应用程序中启动自己的线程。

    【讨论】:

    • 我现在已经切换到 Postgres,默认是最大 100 个连接。根据你的公式,假设我只启动一个并发为 50 的 Sidekiq 进程,53 应该足够了?矿池价值如何在这里发挥作用? pool 是否应该始终低于或等于 max_connections,因此 sidekiq_concurrency * sidekiq_processes 也应该始终低于 pool(因此,max_connections)?
    • pool 应设置为 单个进程 需要的最大值,即 50。连接池是惰性的,因此即使您的 Unicorn 进程池设置为 50,它也会只创建一个连接。
    • 所以,我已将 PostgreSQL 设置为 2000 个最大连接数。我有一个 100 个池大小和 3 个独角兽工人。一个并发数为 50 的 Sidekiq 进程。不过,在一段时间后(例如,一天的测量),作业失败并出现相同的错误,没有获得连接。可能是旧连接没有正确关闭吗?我能做些什么来检查和防止这种情况发生?
    • 我会设置 Postgres 在 N 分钟后关闭空闲连接。它不能解决问题,但它可能会解决它。
    【解决方案2】:

    我确信 sidekiq @MikePerham 的创建者非常适合解决您的 sidekiq 问题,但作为 ruby​​ 开发人员,有两件事很突出。

    如果您通过 ruby​​ 执行大量数据库操作,您可以将其中一些以 triggers 的形式推送到数据库中吗?当然,您仍然可以使用 sidekiq 进程在应用程序端启动它们。 :)

    每隔半小时就会向我尖叫一个通过 cron 运行的 rake 任务。希望你也这样做。 FWIW 我通常使用Whenever gem 创建我必须放入运行应用程序的用户的 crontab 的 cron 行。请注意,它旨在在脚本部署中自动创建 crontask,但在非脚本部署中,您仍然可以利用它通过whenever 命令为您提供必须粘贴到 crontab 中的行。

    您还提到这是用于测量的。

    您是否考虑过利用 elasticsearchsearchkick gem 之类的东西?这是一个稍微复杂一点的设置,请务必为您安装 ES 的服务器设置防火墙。但这可能会使您的代码随着您的成长而更易于管理。此外,它几乎免费为您提供了一个很好的搜索机制,并且它的分布式和更多的语言不可知论,例如BloodhoundJava。 :) 加上kibana 为您提供了一个了解 ES 记录的好窗口

    【讨论】:

    • 感谢您的回答。我认为触发器不会有帮助,因为我创建的所有数据库条目都非常不同。我已经使用任何时间和 IceCube 来安排测量事件。不过,我不确定 Elasticsearch 会如何帮助我。当然,我可以在应用程序中构建一个搜索引擎,但这不是我所做工作的主要用例。
    • 基本上是aggreations。这是一个Nice Writeup,它允许您使用它类似于石墨/statsd 设置。如果你所拥有的东西正在工作,那就没有必要了,只是一个想法。
    猜你喜欢
    • 1970-01-01
    • 2021-10-13
    • 1970-01-01
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 2014-11-21
    • 1970-01-01
    相关资源
    最近更新 更多