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

Node.js中Redis连接复用方案:工具函数还是中间件?

Redis连接复用:工具函数是更适合你的方案

你当前的问题是每次请求都重复创建Redis连接,既冗余又会导致过多连接占用资源。工具函数和中间件都能解决这个问题,但工具函数更简单直接,完全匹配你的需求。


一、工具函数方案(推荐)

创建一个独立的Redis客户端工具文件,全局复用同一个连接实例,所有控制器直接导入使用即可。

步骤1:创建Redis工具文件

在项目的utils/redisClient.js中编写如下代码:

const redis = require('redis');

// 初始化Redis客户端
const client = redis.createClient('redis://127.0.0.1:6379');

// 监听连接错误
client.on('error', (err) => {
  console.error('Redis连接异常:', err);
});

// 提前完成连接(v4+版本支持自动重连,无需担心断开问题)
(async () => {
  await client.connect();
})();

module.exports = client;

步骤2:修改控制器代码

删掉原来重复的Redis连接代码,直接导入工具文件中的客户端实例:

const asyncHandler = require('../middleware/async');
const Profile = require('../models/Profile.model');
const ErrorResponse = require('../utils/errorResponse');
// 导入全局Redis客户端
const redisClient = require('../utils/redisClient');

//@desc     Create a Profile
//@route    POST /api/profile
//@access   Private
exports.createProfile = asyncHandler(async (req, res, next) => {
  // 直接复用已连接的客户端
  const cached = await redisClient.get('profiles');

  const { biography, profileImageUrl, location, birthdate } = req.body;
  const profileFields = {
    user_id: req.user.id,
    biography,
    location,
    birthdate,
    profileImageUrl
  };

  let profile = await Profile.findOne({ where: { user_id: req.user.id } });

  if (profile) {
    // 修正Sequelize的update语法:直接传入更新字段
    profile = await profile.update(profileFields, { new: true });
  } else {
    // 补充else逻辑,避免重复创建数据
    profile = await Profile.create(profileFields);
  }

  // 清除缓存,确保下次请求获取最新数据
  await redisClient.del('profiles');
  res.status(200).json(profile);
});    

//@desc      Get all Profiles
//@route     GET /api/profile
//@access    Public
exports.getProfiles = asyncHandler(async (req, res, next) => {
  const cached = await redisClient.get('profiles');
  
  if(cached) {
    console.log('Profiles cached');
    return res.status(200).json(JSON.parse(cached));
  }

  const profiles = await Profile.findAll();
  console.log('Profiles not cached');
  
  // 先写入缓存再返回响应,避免res.send后代码终止执行
  await redisClient.SETEX('profiles', 3600, JSON.stringify(profiles));
  res.status(200).json(profiles);
});

二、中间件方案(可选)

如果需要将Redis客户端与请求上下文绑定(比如根据请求参数切换连接),可以用中间件将客户端挂载到req对象上,但这个方案对你当前需求来说略显冗余。

步骤1:创建Redis中间件

在middleware/redis.js中编写:

const redis = require('redis');

const client = redis.createClient('redis://127.0.0.1:6379');
client.on('error', (err) => console.error('Redis错误:', err));
(async () => {
  await client.connect();
})();

// 中间件:将客户端挂载到req对象
const redisMiddleware = (req, res, next) => {
  req.redisClient = client;
  next();
};

module.exports = redisMiddleware;

步骤2:在路由中挂载中间件

在routes/profile.js中为需要Redis的路由添加中间件:

const express = require('express');
const router = express.Router();
const redisMiddleware = require('../middleware/redis');
const profileController = require('../controllers/profile');

router.post('/', redisMiddleware, profileController.createProfile);
router.get('/', redisMiddleware, profileController.getProfiles);

module.exports = router;

步骤3:控制器中使用req上的客户端

控制器里直接调用req.redisClient即可:

// createProfile中
const cached = await req.redisClient.get('profiles');
await req.redisClient.del('profiles');

// getProfiles中
const cached = await req.redisClient.get('profiles');
await req.redisClient.SETEX('profiles', 3600, JSON.stringify(profiles));

方案对比

  • 工具函数:代码简洁,全局复用单一连接实例,维护成本低,是你当前场景的最优解。
  • 中间件:适合需要与请求上下文深度绑定的复杂场景,比如动态切换Redis实例,但会增加不必要的代码层级,对你来说没必要。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 06:23:33