扶手:每个请求开关连接,但保持一个连接池 [英] Rails: switch connection on each request but keep a connection pool

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

问题描述

在我们的Rails应用程序,我们需要使用不同的数据库根据要求(每个国家不同的DB)的子域。

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.

但<一个href="https://github.com/rails/rails/blob/11c697383e6fea7834ee1a8ba2815ff0da20f0fe/activerecord/lib/active_record/connection_handling.rb#L37-L47">it似乎 的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

我运行此脚本导轨亚军,并指向一个本地的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的

推荐答案

嗯,我一直在挖掘到这多一点,并设法得到的东西的工作。

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

看完<一href="http://tenderlovemaking.com/2011/10/20/connection-management-in-activerecord.html">tenderlove's帖子有关连接管理的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的<一个href="http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionHandler.html">ConnectionHandler和使用该新的连接处理程序在我的模型层次结构的顶部(一些摆弄上ConnectionHandler <一href="https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L366">$c$c需要了解它的内部工作原理,所以当然这种解决方案可以很我使用绑定到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

这仍然需要一些测试,以检查是否有其实是一个性能提升,但我的本地麒麟服务器上运行的初步测试表明存在。

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.

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

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