Rails:在每个请求上切换连接但保留一个连接池 [英] Rails: switch connection on each request but keep a connection pool

查看:15
本文介绍了Rails:在每个请求上切换连接但保留一个连接池的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我们的 Rails 应用程序中,我们需要根据请求的子域(每个国家/地区不同的数据库)使用不同的数据库.

In our Rails application we need to use different databases depending on the subdomain of the request (different DB per country).

现在我们正在做类似于这个问题中推荐的操作.也就是说,在每个请求上调用 ActiveRecord::Base.establish_connection.

Right now we're doing something similar to what's recommended in this question. That is, calling ActiveRecord::Base.establish_connection on each request.

但是它似乎a> ActiveRecord::Base.establish_connection 删除当前的连接池并在每次调用时建立一个新的连接.

But it seems ActiveRecord::Base.establish_connection drops the current connection pool and establishes a new connection each time it's called.

我做了这个快速基准测试,看看每次调用 establish_connection 和已经建立连接之间是否有任何显着差异:

I made this quick benchmark to see if there was any significant difference between calling establish_connection each time and having the connections already established:

require 'benchmark/ips'

$config = Rails.configuration.database_configuration[Rails.env]
$db1_config = $config.dup.update('database' => 'db1')
$db2_config = $config.dup.update('database' => 'db2')

# Method 1: call establish_connection on each "request".
Benchmark.ips do |r|
  r.report('establish_connection:') do
    # Simulate two requests, one for each DB.
    ActiveRecord::Base.establish_connection($db1_config)
    MyModel.count # A little query to force the DB connection to establish.
    ActiveRecord::Base.establish_connection($db2_config)
    MyModel.count
  end
end

# Method 2: Have different subclasses of my models, one for each DB, and 
# call establish_connection only once
class MyModelDb1 < MyModel
  establish_connection($db1_config)
end

class MyModelDb2 < MyModel
  establish_connection($db2_config)
end

Benchmark.ips do |r|
  r.report('different models:') do
    MyModelDb1.count
    MyModelDb2.count
  end
end

我用 rails runner 运行这个脚本,并指向一个本地 mysql,数据库上有几千条记录,结果似乎表明实际上有很大的不同(顺序量级)在两种方法之间(顺便说一句,我不确定基准测试是否有效,或者我搞砸了,因此结果具有误导性):

I run this script with rails runner and pointing to a local mysql with some couple thousand records on the DBs and the results seem to indicate that there in fact is a pretty big difference (of an order of magnitude) between the two methods (BTW, i'm not sure if the benchmark is valid or i screwed up and therefore the results are misleading):

Calculating -------------------------------------
establish_connection: 8 i/100ms
-------------------------------------------------
establish_connection: 117.9 (±26.3%) i/s -        544 in   5.001575s
Calculating -------------------------------------
    different models:  119 i/100ms
-------------------------------------------------
    different models:  1299.4 (±22.1%) i/s -       6188 in   5.039483s

所以,基本上,我想知道是否有办法为每个子域维护一个连接池,然后重用这些连接而不是在每个请求上建立新连接.为每个子域设置一个我的模型的子类是不可行的,因为有很多模型;我只想更改所有模型的连接(在 ActiveRecord::Base 中)

So, basically, i'd like to know if there's a way to maintain a connection pool for each subdomain and then re-use those connections instead of establishing a new connection on each request. Having a subclass of my models for each subdomain is not feasible, as there are many models; i just want to change the connection for all the models (in ActiveRecord::Base)

推荐答案

嗯,我一直在深入研究这个问题,并设法让一些东西工作起来.

Well, i've been digging into this a bit more and managed to get something working.

阅读tenderlove 的帖子后,关于连接管理ActiveRecord 解释了类层次结构如何与连接管理不必要地耦合,我明白为什么我想做的事情不像人们预期的那么简单.

After reading tenderlove's post about connection management in ActiveRecord, which explains how the class hierarchy gets unnecessarily coupled with the connection management, i understood why doing what i'm trying to do in not as straightforward as one would expect.

我最终做的是子类化 ActiveRecord 的 ConnectionHandler 并使用它我的模型层次结构顶部的新连接处理程序(对 ConnectionHandler 代码 需要了解它的内部工作原理;当然,这个解决方案可能与我使用的 Rails 版本 (3.2) 密切相关.类似的东西:

What i ended up doing was subclassing ActiveRecord's ConnectionHandler and using that new connection handler at the top of my model hierarchy (some fiddling on the ConnectionHandler code was needed to understand how it works internally; so of course this solution could be very tied to the Rails version i'm using (3.2)). Something like:

# A model class that connects to a different DB depending on the subdomain 
# we're in
class ModelBase < ActiveRecord::Base
  self.abstract_class = true
  self.connection_handler = CustomConnectionHandler.new
end

# ...

class CustomConnectionHandler < ActiveRecord::ConnectionAdapters::ConnectionHandler
  def initialize
    super
    @pools_by_subdomain = {}
  end

  # Override the behaviour of ActiveRecord's ConnectionHandler to return a
  # connection pool for the current domain.
  def retrieve_connection_pool(klass)
    # Get current subdomain somehow (Maybe store it in a class variable on 
    # each request or whatever)
    subdomain = @@subdomain
    @pools_by_subdomain[subdomain] ||= create_pool(subdomain)
  end

  private
  def create_pool(subdomain)
    conf = Rails.configuration.database_configuration[Rails.env].dup
    # The name of the DB for that subdomain...
    conf.update!('database' => "db_#{subdomain}")
    resolver = ActiveRecord::Base::ConnectionSpecification::Resolver.new(conf, nil)
    # Call ConnectionHandler#establish_connection, which receives a key 
    # (in this case the subdomain) for the new connection pool
    establish_connection(subdomain, resolver.spec)
  end
end

这仍然需要一些测试来检查是否确实有性能提升,但我在本地 Unicorn 服务器上运行的初始测试表明有.

This still needs some testing to check if there is in fact a performance gain, but my initial tests running on a local Unicorn server suggest there is.

这篇关于Rails:在每个请求上切换连接但保留一个连接池的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆