在Express框架中如何处理Redis连接错误
问题核心
Redis客户端实例创建为同步逻辑,但连接建立、连接失败均为异步触发事件,因此你当前同步返回实例的逻辑无法感知后续的异步连接结果,才会始终返回已定义的实例对象。
修复后完整代码
let connections = [] const killRedis = (redisClient, ip, port) => { redisClient.quit() connections = connections.filter((i) => { return i.ip !== ip && i.port != port }) } // 订阅连接事件,修改对应连接的状态 const subscribe = (connItem) => { const { connection: redisClient, ip, port } = connItem redisClient.on('error', function (err) { connItem.status = 'failed' killRedis(redisClient, ip, port) // 触发等待队列的错误回调 if (connItem.waiters) { connItem.waiters.forEach(({ reject }) => reject(new Error(`Redis连接失败: ${err.message}`))) delete connItem.waiters } }); redisClient.on('connect', function () { connItem.status = 'connected' console.log('Connected to Redis'); // 触发等待队列的成功回调 if (connItem.waiters) { connItem.waiters.forEach(({ resolve }) => resolve(redisClient)) delete connItem.waiters } }); } const findConnection = (ip, port) => { // 过滤掉已失败的连接记录 let connection = connections.filter(i => i.ip == ip && i.port == port && i.status !== 'failed') if (connection && connection.length > 0) { return connection[0] } else { const connItem = { ip: ip, port: port, status: 'pending', connection: require('redis').createClient(port, ip, { no_ready_check: true, db: 1 }), waiters: [] // 存储同个连接的并发等待回调 } connections.push(connItem) subscribe(connItem) return connItem } } const createRedisClient = async (port, url) => { const connItem = findConnection(url, port) // 已经连接成功直接返回 if (connItem.status === 'connected') { return connItem.connection } // 连接失败直接抛出错误 if (connItem.status === 'failed') { throw new Error('Redis连接失败') } // 还在连接中,加入等待队列 return new Promise((resolve, reject) => { connItem.waiters.push({ resolve, reject }) }) } module.exports = { createRedisClient }
核心修改点
- 给连接池的每个记录新增
status字段,标记pending/connected/failed三种状态,无需依赖Redis客户端的内部私有属性判断连接有效性 - 新增
waiters队列处理并发请求同个未就绪连接的场景,避免重复创建客户端 - 异步返回结果,连接成功返回客户端实例,连接失败直接抛出错误,调用方可通过
try/catch捕获异常
调用方式示例
// 业务代码中调用 const { createRedisClient } = require('./redis') async function test() { try { const client = await createRedisClient(6379, '127.0.0.1') // 连接成功后才会执行到此处,可正常操作Redis client.set('test', '123') } catch (e) { // 连接失败会进入此处,可自定义业务容错逻辑 console.error('连接Redis失败', e.message) } }
内容的提问来源于stack exchange,提问作者mouchin777
相关产品推荐
相关产品推荐

