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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 21:27:12