繁体   English   中英

如何让Rails在数据库停机后自动重新建立数据库连接

[英]How to get Rails to automatically reestablish database connections after a database downtime

在数据库停机后,Rails将首先抛出此错误一次:

ActiveRecord :: StatementInvalid:NativeException:org.postgresql.util.PSQLException:连接被拒绝。 检查主机名和端口是否正确以及postmaster是否接受TCP / IP连接。

从那时起,即使在数据库备份之后,每次调用数据库都会出现以下错误:

ActiveRecord :: StatementInvalid:ActiveRecord :: JDBCError:此连接已关闭。

为了让服务器再次运行,我必须重新启动rails服务器。 这对我们来说并不理想,因为我们的产品工程师希望对我们的数据库进行维护,而不必重新启动依赖于数据库的所有服务。 所以,我想知道 - 有没有办法自动让Rails尝试重新建立数据库连接或建议的方式来获得这种行为?

我尝试过的事情:

我已经尝试在我的数据库选项中将reconnect设置为true,然后我可以杀死单个数据库连接,rails将重新建立连接。 但是,它不会在数据库中断之后。 我发现从命令控制台我可以通过调用来恢复连接

的ActiveRecord :: Base的:: establish_connection

所以也许找到一个干净的地方让rails调用上面的命令,这会有用吗? 有什么建议?

这是一个非常难看的解决方案,但我认为它应该有效。

  • 将reconnect设置为true,就像之前一样。

  • 编辑文件activerecord-XYZ/lib/active_record/connection_adapters/postgresql_adapter.rb并更改reconnect! 方法说

     def reconnect! clear_cache! ActiveRecord::Base.establish_connection end 

需要更多的研究

  • 检查它是否真的有效
  • 检查它是否同时多次调用establish_connection(在这种情况下你需要一个锁)
  • 检查是否有更好的地方放置此代码.Ruby允许您在运行时重新定义任何方法,但您需要加载符号。 换句话说,您需要PostgreSQLAdapter类存在。 initialize!后,我最接近加载该符号的是config/environment.rb initialize! ,但是在堆栈中仍然没有足够的深度来加载该符号。

如果在ActiveRecord代码之外找到已加载符号的位置,并且可以编辑其方法,则将以下代码放入其中:

class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::StatementPool
  def reconnect!
    clear_cache!
    ActiveRecord::Base.establish_connection
  end
end

更重要的是,实际调用establish_connection有点过分。 有可能在该方法中调用重要的东西 ,以避免一些开销。

如果这有帮助,请告诉我,如果你取得了任何进展。

我和Mysql2Adapter有同样的问题。 我通过执行一个非常长的查询来复制失败: User.find_all_by_id((1..1000000).to_a) ; 从现在开始,所有ActiveRecord请求都失败(User.first失败)

这是我解决它的方式:

问题非常简单:每当我们得到上述异常时,我们都希望重新建立连接并重试。 我们通过别名执行方法,使用begin rescue包装它,并在救援中重新建立数据库连接来解决问题。

对于Mysql,代码在Mysql2Adapter中,下面是修复:

将此代码放在config / initializers / active_record.rb中

module ActiveRecord
  module ConnectionAdapters
    class Mysql2Adapter < AbstractMysqlAdapter
      alias_method :old_execute, :execute

      def execute(sql, name=nil)
        begin
          old_execute(sql, name)
        rescue ActiveRecord::StatementInvalid
          # you can do some logging here

          ActiveRecord::Base.establish_connection

          old_execute(sql, name)
        end
      end
    end
  end
end

你需要为postgres适配器做同样的事情。

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb

我认为因为select是最常用的查询,所以你可以选择别名,一旦失败,就会重新建立连接。

你想创建一个初始化程序(config / initializers / active_record.rb)和别名select_rows(它可能是其他的东西,只需找到正确的方法并对其进行修补。它可能是async_exec或执行,我对Postgres的看法并不多'适配器):

module ConnectionAdapters::PostgreSQLAdapter
      module DatabaseStatements
      end
end

在某处插入救援

rescue ActiveRecord::StatementInvalid: ActiveRecord::JDBCError
  ActiveRecord::Base::establish_connection
  retry

但是哪里? 我不知道

您也可以在ApplicationController中使用rescue。 但这不会重试失败的操作,因此您可能还应该渲染一些错误模板

rescue_from ActiveRecord::StatementInvalid: ActiveRecord::JDBCError do
  ActiveRecord::Base::establish_connection
  render 'errors/error', :status => 500
end

我不确定如何完全按照你的要求做,但我有另一个'过程'建议:设置简单的脚本,这样你的产品工程师就可以轻松地停止并启动所有应用程序。

开发一套capistrano配方(或其他脚本),您的产品工程师可以使用它来停止和启动所有应用程序。 对于普通的Rails应用程序,您真正需要做的就是放置维护页面,以便nginx或apache服务该页面而不是将请求转发到rails实例。 理想情况下,rails worker停止获取请求,db关闭,db出现,然后维护页面被取消,工作人员再次获得请求,从未意识到数据库已经离开了一段时间。

对于后台工作者,他们可能需要实际停止并由脚本启动,除非他们的队列为空且保持为空。 任何计划的rake任务或其他预定作业如果依赖于数据库并在其停止运行时可能会失败,因此当您正常进行数据库维护时,您将需要尝试安排它们在窗口外运行。

如果你的prod工程师不喜欢运行脚本(!),你可能会设置一个漂亮的Web界面来让它们变得简单。 这可能不仅仅对处理数据库连接错误有用,因为它将使组织中的更多人能够处理停止和启动应用程序等基本操作。

我建议使用外部工具/脚本来监控此类事件。 Activerecord的重新连接在数据库在某个空闲时间之后终止连接时起作用,并且在某些失败时间后它将放弃。 所以它对你的情况没有帮助。

我认为您应该编写自己的脚本来监视数据库的状态。 如果它在一段时间后回来,只需重启rails应用程序即可。

此外,你还需要那些监控内容,比如服务器的内存和CPU和磁盘使用,服务器负载,数据库状态,一堆东西。 只是一个稍微定制的监控规则。

暂无
暂无

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

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