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

Node.js应用使用Redis客户端缓存时遭遇ClientClosedError问题求助

解决Redis ClientClosedError: The client is closed错误

问题场景

以下是我Node.js项目index.js中的代码:

const express = require('express');
const axios = require('axios');
const cors = require('cors');
const redis = require('redis');

const port = 3000;
const DEFAULT_EXPIRATION = 3600;

const redisClient = redis.createClient();

redisClient.on('error', (error) => {
    console.error('Redis error:', error);
});

redisClient.on('connect', () => {
    console.log('Connected to Redis server');
});

const app = express();
app.use(cors());

app.get("/photos", async (req, res) => {
    try {
        const albumId = req.query.albumId;
        const { data } = await axios.get(
            "https://jsonplaceholder.typicode.com/photos",
            { params: { albumId } }
        );

        redisClient.set('photos', JSON.stringify(data));
        redisClient.expire('photos', DEFAULT_EXPIRATION);

        res.json(data);
    } catch (error) {
        console.error("Error fetching photos:", error);
        res.status(500).json({ error: "Failed to fetch photos" });
    }
});

app.get("/photos/:id", async (req, res) => {
    try {
        const photoId = req.params.id;
        const { data } = await axios.get(
            `https://jsonplaceholder.typicode.com/photos/${photoId}`
        );

        res.json(data);
    } catch (error) {
        console.error("Error fetching photo:", error);
        res.status(500).json({ error: "Failed to fetch photo" });
    }
});

app.listen(port, () => {
    console.log(`Server running on port ${port}`);
});

运行时触发如下错误:

return Promise.reject(new errors_1.ClientClosedError());
^
ClientClosedError: The client is closed

已导入createClient方法,但问题未解决,需要排查并修复。

问题原因及修复方案

原因

Redis v4+版本中,createClient()创建实例后不会自动连接,必须手动调用connect()方法;同时旧版的connect事件监听也不再适用,需要改用ready事件。

修复步骤

  1. 手动调用connect()并处理连接逻辑
    将Redis客户端的初始化代码修改为异步方式,确保连接成功后再启动Express服务:

    const redisClient = redis.createClient();
    
    // 替换connect事件为ready事件
    redisClient.on('ready', () => {
        console.log('Connected to Redis server');
        // 连接成功后启动服务
        app.listen(port, () => {
            console.log(`Server running on port ${port}`);
        });
    });
    
    redisClient.on('error', (error) => {
        console.error('Redis error:', error);
    });
    
    // 手动触发连接
    (async () => {
        try {
            await redisClient.connect();
        } catch (err) {
            console.error('Failed to connect to Redis:', err);
            process.exit(1); // 连接失败时退出进程
        }
    })();
    
  2. 使用Promise/async-await处理Redis命令
    Redis v4+的命令返回Promise,建议改用await确保命令执行成功,避免因客户端未就绪导致的错误:
    在/photos接口中修改Redis操作:

    // 替换原有的set和expire为await方式,直接设置过期时间
    await redisClient.set('photos', JSON.stringify(data), {
        EX: DEFAULT_EXPIRATION
    });
    
  3. 可选:指定Redis连接参数
    如果Redis服务不在本地默认端口,需要在createClient中传入配置:

    const redisClient = redis.createClient({
        url: 'redis://localhost:6379' // 根据实际地址修改
    });
    

完整修复后的代码示例

const express = require('express');
const axios = require('axios');
const cors = require('cors');
const redis = require('redis');

const port = 3000;
const DEFAULT_EXPIRATION = 3600;

const redisClient = redis.createClient();

redisClient.on('ready', () => {
    console.log('Connected to Redis server');
    app.listen(port, () => {
        console.log(`Server running on port ${port}`);
    });
});

redisClient.on('error', (error) => {
    console.error('Redis error:', error);
});

(async () => {
    try {
        await redisClient.connect();
    } catch (err) {
        console.error('Failed to connect to Redis:', err);
        process.exit(1);
    }
})();

const app = express();
app.use(cors());

app.get("/photos", async (req, res) => {
    try {
        const albumId = req.query.albumId;
        const { data } = await axios.get(
            "https://jsonplaceholder.typicode.com/photos",
            { params: { albumId } }
        );

        await redisClient.set('photos', JSON.stringify(data), {
            EX: DEFAULT_EXPIRATION
        });

        res.json(data);
    } catch (error) {
        console.error("Error fetching photos:", error);
        res.status(500).json({ error: "Failed to fetch photos" });
    }
});

app.get("/photos/:id", async (req, res) => {
    try {
        const photoId = req.params.id;
        const { data } = await axios.get(
            `https://jsonplaceholder.typicode.com/photos/${photoId}`
        );

        res.json(data);
    } catch (error) {
        console.error("Error fetching photo:", error);
        res.status(500).json({ error: "Failed to fetch photo" });
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 11:44:52