简体   繁体   English

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

[英]Ensure max database connections is not exceeded with Rails and Sidekiq

I am using Nginx with Phusion Passenger with a single-threaded Rails application. 我正在使用带有单线程Rails应用程序的Nginx和Phusion Passenger。 Here's the catch. 这是捕获。 Within that application, I am using multi-threaded sidekiq to perfrom some background jobs. 在该应用程序中,我使用多线程sidekiq来执行一些后台作业。 Typically in my database.yml, I would only need to set the pool value to 1. Here's an example: 通常在我的database.yml中,我只需要将池值设置为1.这是一个例子:

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

The reason is because for each tcp socket connection opened, when an http request comes in through that socket, nginx will take the request and pass the information to passenger. 原因是因为每个tcp套接字连接打开,当http请求通过该套接字进入时,nginx将接收请求并将信息传递给乘客。 Passenger detects its a Rails app, and it spawns a Rails instance, which converts the response to html, which is sent back to nginx, which is then passed back to the client (browser) So for each passenger instance, I will only need one database connection, with a single-threaded Rails app. Passenger检测到它的Rails应用程序,它产生一个Rails实例,它将响应转换为html,然后发送回nginx,然后传回给客户端(浏览器)。因此对于每个乘客实例,我只需要一个数据库连接,使用单线程Rails应用程序。

But in my sidekiq.yml, I have set concurrency to 5: 但是在我的sidekiq.yml中,我将并发设置为5:

:concurrency: 5 

This means for each passenger rack instance, I will have 5 concurrent threads handled by sidekiq plus the one connection for the main app, that is a total of 6 database connections for one passenger instance. 这意味着对于每个乘客机架实例,我将有由sidekiq处理的5个并发线程加上主应用程序的一个连接,即一个乘客实例的总共6个数据库连接。

When I look at passenger-status, I notice that max_pool_size is set to 6: 当我查看乘客状态时,我注意到max_pool_size设置为6:

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

So does that mean passenger will never spawn more than 6 Rails instances concurrently? 那么这是否意味着乘客不会同时产生超过6个Rails实例? And if that's the case, does that mean my math is correct: 6 (instances) * 6 (database connections: 5 for sidekiq and 1 for main app) = 36 (total database connections possible for my rails app to handle concurrently). 如果是这样的话,这是否意味着我的数学是正确的:6(实例)* 6(数据库连接:5为sidekiq,1为主app)= 36(我的rails应用程序可以同时处理的总数据库连接)。

Right now my mysql database is configured to handle 151 max concurrent connections. 现在我的mysql数据库配置为处理151个最大并发连接。

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

I just want to make sure my math is correct regarding passenger, rails and sidekiq. 我只是想确保我的数学是正确的乘客,铁路和sidekiq。

First of all, your Sidekiq processes and your web server (in your case, Passenger) are separate. 首先,您的Sidekiq流程和您的Web服务器(在您的情况下,Passenger)是分开的。 Passenger's thread pool size has no effect on your Sidekiq concurrency; Passenger的线程池大小对您的Sidekiq并发性没有影响; instead, your Sidekiq configuration specifies a separate concurrency. 相反,您的Sidekiq配置指定单独的并发。 So, we'll consider the two separately: 所以,我们将分别考虑这两个:

Passenger 乘客

The ActiveRecord database pool value is the number of database connections that your web process will use, in total across all threads . ActiveRecord数据库池值是Web进程将在所有线程中使用的数据库连接数。 If your Passenger server is set up in multi-process mode, then your max connections from your web processes is db pool size * passenger pool size . 如果您的Passenger服务器设置为多进程模式,则Web进程的最大连接数为db pool size * passenger pool size On the other hand, if you set it up in multi-threaded mode (which I'd recommend if possible), your max connections is just db pool size (multiplied by however many processes are running; Puma, for example, runs by default two processes with up to fifteen threads or so, so the max connections in that case would be 30). 另一方面,如果你在多线程模式下设置它(我建议如果可能),你的最大连接只是db pool size (乘以许多进程正在运行;例如,Puma默认运行)两个进程最多包含15个线程,因此在这种情况下最大连接数为30)。

So, if you're using multi-threaded mode, a pool size of 1 is absolutely not sufficient -- you'll want at least as big a pool as you expect to have threads. 因此,如果您使用的是多线程模式,则池大小为1是绝对不够的 - 您至少需要与预期的线程一样大的池。 In multi-process mode, 1 might work but I doubt it's really worth straying from the default of 5 , until you encounter issues. 在多进程模式中, 1 可能有效,但我怀疑它是否真的值得从默认值5消失,直到遇到问题。

Sidekiq Sidekiq

Sidekiq always runs in multi-threaded mode (you can technically run multiple processes as well, but I'll assume you aren't). Sidekiq总是以多线程模式运行(你可以在技术上运行多个进程,但我认为你不是)。 So, like above, you want your connection pool to be at least as big as the number of threads. 因此,如上所述,您希望连接池至少与线程数一样大。 This might mean that you technically need two different values for your db pool value depending on whether the Rails env is spinning up for Passenger, or for Sidekiq -- see this issue on the Sidekiq repo or this helpful Heroku guide for more information on how to address that. 这可能意味着您在技术上需要两个不同的db池值值,具体取决于Rails env是为Passenger启动还是为Sidekiq启动 - 请参阅Sidekiq repo上的此问题此有用的Heroku指南 ,了解有关如何使用的更多信息解决这个问题

In summary 综上所述

Don't forget that, aside from all the above, you may easily have multiple servers all running the same Rails app, but only one database with one connection limit. 不要忘记,除了上述所有内容之外,您可以轻松地让多个服务器都运行相同的Rails应用程序,但只有一个数据库具有一个连接限制。 If you're running Passenger in multi-instance mode with a max of 6 processes, set your db pool size to 5, then each web server node will use up to 30 connections. 如果您在多实例模式下运行Passenger,最多有6个进程,请将数据库池大小设置为5,然后每个Web服务器节点最多使用30个连接。 If it's running a Sidekiq server, then add 5 to that. 如果它正在运行Sidekiq服务器,那么添加5。 You will probably not need more than one Sidekiq server, so 4 web nodes @ 30 connections + one Sidekiq process @ 5 connections = 125 maximum connections, well within your MySQL connection limit. 您可能不需要多个Sidekiq服务器,因此4个Web节点@30个连接+一个Sidekiq进程@ 5个连接= 125个最大连接,完全在您的MySQL连接限制内。

I reviewed the Passenger documentation again, and while the answer above answers the question, I want to add a little more detail: 我再次回顾了Passenger文档,虽然上面的答案回答了这个问题,但我想补充一点细节:

  • HTTP client via TCP sends a request to Nginx HTTP客户端通过TCP向Nginx发送请求
  • Phusion Passenger loaded into Nginx checks if request should be handled by Passenger. 加载到Nginx的Phusion Passenger检查请求是否应由Passenger处理。 If so, request is sent to Passenger Core. 如果是,请求将发送到Passenger Core。
  • Passenger core, using load balancing rules, determines which process a request should be forwarded to. 使用负载平衡规则的乘客核心确定应将请求转发到哪个进程。
  • Passenger core also takes care of application spawning: if it determines that having more application processes is necessary or beneficial, then it will make that happen subject to user-configured limits: the core will never spawn more processes than a user-configured maximum. Passenger核心还负责应用程序生成:如果它确定有更多的应用程序进程是必要的或有益的,那么它将根据用户配置的限制进行:核心永远不会产生比用户配置的最大值更多的进程。
  • Passenger core also has monitoring and statistics: passenger-memory-stats and passenger-status 客运核心还有监测和统计数据:乘客记忆统计和乘客身份
  • Passenger core restarts an application process if it crashes. 如果应用程序进程崩溃,则会重新启动应用程序进程。
  • UstRouter sits idle and does not consume resources if you did not configure it to send data to Union Station, a monitoring web service 如果您没有将其配置为向联合站(一种监控Web服务)发送数据,则UstRouter处于空闲状态并且不会消耗资源
  • Watchdog monitors Passenger Core and UstRouter. Watchdog监控Passenger Core和UstRouter。 If either of them crash, they are restarted by the Watchdog. 如果它们中的任何一个崩溃,它们将由Watchdog重新启动。

    passenger-memory-stats will verify the three aforementioned processes as well as the spawned rack apps: 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 shows that the max_pool_size is 6. That is, at most there will be 6 rack apps spawned by Passenger Core: 乘客状态显示max_pool_size为6.也就是说,乘客核心最多会产生6个机架应用程序:

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

As stated in another answer, the ActiveRecord database pool value is the number of database connections that your web process will use, in total across all threads. 如另一个答案所述,ActiveRecord数据库池值是Web进程将在所有线程中使用的数据库连接数。

But since I am using the free Passenger server, which is set up in multi-process mode, then my max connections from my web processes is db pool size * passenger pool size. 但由于我使用的是在多进程模式下设置的免费Passenger服务器,因此我的Web进程的最大连接数是db pool size *乘客池大小。 So since Passenger pool size is 6, and if my db pool size is 1, that is 6 * 1 = 6. That will be 6 maximum database connections. 因此,由于乘客池大小为6,如果我的数据库池大小为1,则为6 * 1 = 6.这将是6个最大数据库连接。

Sidekiq always runs in multi-threaded mode. Sidekiq始终以多线程模式运行。

If someone wants to use sidekiq they must configure the number of threads they want to run on or use the default (25). 如果有人想使用sidekiq,他们必须配置他们想要运行的线程数或使用默认值(25)。 If they are using a database (likely) then to not hit a connection timeout error they will need to have at least as many connections in their database pool as sidekiq threads. 如果他们正在使用数据库(可能)然后没有遇到连接超时错误,他们将需要在其数据库池中至少具有与sidekiq线程一样多的连接。 Currently they must configure these two values in two different places, database pool in database.yml for ActiveRecord, and sidekiq connections either via command line or the sidekiq yml file. 目前,他们必须在两个不同的位置配置这两个值,数据库池在database.yml中用于ActiveRecord,而sidekiq连接可以通过命令行或sidekiq yml文件。 This is a problem as it is difficult to remember when you are modifying one value that you need to modify both. 这是一个问题,因为当您修改需要修改两个值的一个值时很难记住。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM