You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails健康检查遇Mysql2连接错误阻塞,如何保留其他组件检查?

Rails多组件健康检查端点实现(避免DB异常阻塞其他检查)

问题背景

我们需要给Rails应用搭建一个覆盖数据库、Sidekiq、Redis、缓存的健康检查端点,同时支持HTML视图和JSON响应两种输出格式。最初的数据库检查逻辑无法处理Rails/Mysql2 gem提前抛出的连接异常,而使用控制器全局rescue_from的方式会直接终止请求,导致其他组件的检查无法执行。

解决方案

核心思路是将数据库连接异常的捕获逻辑封装到检查方法内部,不让异常扩散到控制器层面,确保所有组件的检查都能执行完毕后再统一返回结果。

1. 改进数据库检查方法

直接在run_db_check中主动尝试建立连接并捕获所有相关异常,避免依赖仅能检查连接对象是否存在的connection.present?:

def run_db_check
  # 主动验证连接有效性,执行简单查询确保连接可用
  ActiveRecord::Base.connection.verify!
  ActiveRecord::Base.connection.exec_query('SELECT 1')
  { check_name: 'database', ok: true }
rescue ActiveRecord::NoDatabaseError,
       ActiveRecord::ConnectionNotEstablished,
       Mysql2::Error => e
  { check_name: 'database', ok: false, error_details: "#{e.class}: #{e.message}" }
rescue StandardError => e
  { check_name: 'database', ok: false, error_details: e.class.to_s }
end

2. 完整健康控制器实现

将所有组件的检查方法独立封装,统一收集结果后生成响应,同时支持多格式输出:

class HealthController < ApplicationController
  def index
    @check_results = [
      run_db_check,
      run_redis_check,
      run_sidekiq_check,
      run_cache_check
    ]
    @overall_ok = @check_results.all? { |r| r[:ok] }

    respond_to do |format|
      format.html { render status: @overall_ok ? :ok : :internal_server_error }
      format.json do
        render json: {
          ok: @overall_ok,
          details: @check_results
        }, status: @overall_ok ? :ok : :internal_server_error
      end
    end
  end

  private

  def run_db_check
    ActiveRecord::Base.connection.verify!
    ActiveRecord::Base.connection.exec_query('SELECT 1')
    { check_name: 'database', ok: true }
  rescue ActiveRecord::NoDatabaseError,
         ActiveRecord::ConnectionNotEstablished,
         Mysql2::Error => e
    { check_name: 'database', ok: false, error_details: "#{e.class}: #{e.message}" }
  rescue StandardError => e
    { check_name: 'database', ok: false, error_details: e.class.to_s }
  end

  def run_redis_check
    # 检查Redis连接有效性
    Redis.current.ping
    { check_name: 'redis', ok: true }
  rescue StandardError => e
    { check_name: 'redis', ok: false, error_details: e.class.to_s }
  end

  def run_sidekiq_check
    # 检查Sidekiq队列可用性(依赖Redis)
    Sidekiq::Queue.all
    { check_name: 'sidekiq', ok: true }
  rescue StandardError => e
    { check_name: 'sidekiq', ok: false, error_details: e.class.to_s }
  end

  def run_cache_check
    # 检查缓存读写有效性
    test_key = "health_check_#{SecureRandom.uuid}"
    Rails.cache.write(test_key, 'ok', expires_in: 10.seconds)
    result = Rails.cache.read(test_key) == 'ok'
    Rails.cache.delete(test_key)
    result ? { check_name: 'cache', ok: true } : { check_name: 'cache', ok: false, error_details: 'Read/write failed' }
  rescue StandardError => e
    { check_name: 'cache', ok: false, error_details: e.class.to_s }
  end
end

3. 关键说明

  • 异常本地化处理:每个检查方法内部捕获自身异常,单个组件故障不会中断整个检查流程
  • 统一结果格式:所有检查返回结构一致的哈希,便于后续汇总和渲染
  • 多格式支持:通过respond_to块同时提供HTML和JSON响应,适配不同场景需求
  • 有效性验证:通过执行实际操作(如数据库查询、Redis ping、缓存读写)验证组件可用性,避免假阳性结果

内容的提问来源于stack exchange,提问作者Cameron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 20:22:40