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
相关产品推荐
相关产品推荐

