NestJS中Express-Session依赖Redis宕机后请求阻塞需优化
解决NestJS中Redis下线导致请求超时的问题
核心思路
当Redis服务不可用时,express-session的Redis存储会阻塞请求直到超时,我们需要通过提前检测Redis状态和捕获session操作错误来直接返回响应,避免请求挂起。
具体实现方案
1. 配置Redis客户端的超时与状态监控
先给Redis客户端设置连接超时、重试限制,同时维护连接状态标识:
const RedisStore = connectRedis(session); const redisClient = new Redis({ url: 'redis://****:6379', connectTimeout: 500, // 连接超时时间(毫秒) maxRetriesPerRequest: 1, // 每个请求最多重试1次 retryDelayOnFailover: 0, // 故障转移时不延迟重试 }); // 维护Redis连接状态 let isRedisAvailable = true; redisClient.on('error', (err) => { console.error('Redis连接错误:', err); isRedisAvailable = false; }); redisClient.on('connect', () => { console.log('Redis连接成功'); isRedisAvailable = true; });
2. 添加前置中间件拦截请求
在session中间件之前添加全局中间件,先判断Redis是否可用,不可用直接返回503错误:
async function bootstrap() { const app = await NestFactory.create(AppModule); // 前置检测Redis状态的中间件 app.use((req, res, next) => { if (!isRedisAvailable) { return res.status(503).json({ code: 503, message: '服务暂时不可用,请稍后重试' }); } next(); }); app.use( session({ store: new RedisStore({ client: redisClient }), secret: 'asdasdasd', resave: false, saveUninitialized: false, cookie: { httpOnly: true, maxAge: 1000 * 60 * 10 }, }), ); await app.listen(3000); }
3. 包装RedisStore方法捕获实时错误
如果Redis在请求过程中突然断开,前置检测无法覆盖,可包装RedisStore的核心方法捕获错误:
const redisStore = new RedisStore({ client: redisClient }); // 包装get方法,捕获Redis操作超时错误 const originalGet = redisStore.get.bind(redisStore); redisStore.get = (sid, callback) => { originalGet(sid, (err, session) => { if (err && err.code === 'ETIMEDOUT') { isRedisAvailable = false; return callback(new Error('Redis服务不可用')); } callback(err, session); }); }; // 包装set方法 const originalSet = redisStore.set.bind(redisStore); redisStore.set = (sid, session, callback) => { originalSet(sid, session, (err) => { if (err && err.code === 'ETIMEDOUT') { isRedisAvailable = false; return callback(new Error('Redis服务不可用')); } callback(err); }); }; // 在session配置中使用包装后的store app.use( session({ store: redisStore, secret: 'asdasdasd', resave: false, saveUninitialized: false, cookie: { httpOnly: true, maxAge: 1000 * 60 * 10 }, }), );
4. 全局异常过滤器统一处理错误
添加NestJS全局异常过滤器,捕获session处理时的Redis错误,统一返回响应:
import { ExceptionFilter, Catch, ArgumentsHost, HttpStatus } from '@nestjs/common'; import { Response } from 'express'; @Catch(Error) export class RedisExceptionFilter implements ExceptionFilter { catch(exception: Error, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); if (exception.message.includes('Redis服务不可用') || exception.code === 'ETIMEDOUT') { response.status(HttpStatus.SERVICE_UNAVAILABLE).json({ code: HttpStatus.SERVICE_UNAVAILABLE, message: '服务暂时不可用,请稍后重试' }); } else { response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({ code: HttpStatus.INTERNAL_SERVER_ERROR, message: '服务器内部错误' }); } } } // 在bootstrap中注册全局过滤器 async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalFilters(new RedisExceptionFilter()); // 其他配置... }
效果说明
配置完成后,当Redis服务下线或连接超时:
- 前置中间件会直接拦截请求,返回503响应
- 若Redis在请求过程中断开,包装后的store方法会捕获错误,触发全局过滤器返回响应
- 不会再出现请求一直加载直到超时的情况
内容的提问来源于stack exchange,提问作者Wenjiao Lee
相关产品推荐
相关产品推荐

