Docker环境下Express/NodeJS无法连接Redis但MySQL正常的问题
问题
核心问题:NodeJS本地运行时可正常连接MySQL和Redis;Docker容器中运行时仅能连接MySQL,无法连接Redis。
Docker Compose配置
version: '3' services: express-app: build: context: . ports: - 3000:3000 depends_on: - redis-db - mysql-db environment: - MYSQL_HOST=mysql-db - REDIS_HOST=redis-db mysql-db: image: mysql:latest environment: MYSQL_DATABASE: twitter MYSQL_USER: user MYSQL_PASSWORD: user-password MYSQL_ROOT_PASSWORD: root-password ports: - '3306:3306' volumes: - my-twitter-db-mount:/var/lib/mysql redis-db: image: redis ports: - '6379:6379' volumes: - my-twitter-redis-mount:/var/lib/redis/data volumes: my-twitter-db-mount: my-twitter-redis-mount:
NodeJS代码
const mysql = require('mysql2'); const redis = require('redis'); var connection; var redisConnection; const getConnection = (() => { console.log("################ "+process.env.MYSQL_HOST) ; console.log("################ "+process.env.REDIS_HOST) ; connection = mysql.createConnection({ host: process.env.MYSQL_HOST || 'localhost', port: 3306, user: 'user', password: 'user-password' }); redisConnection = redis.createClient({ port: 6379, host: process.env.REDIS_HOST || 'localhost' }); connection.connect(); redisConnection.connect(); }); const queryFollowers = (async (ownerId) => { getConnection(); let val = await redisConnection.get(`OwnerCache_${ownerId}`); if (val) { console.log("Returning the cached value "+val); return new Promise((resolve, reject) => resolve(val)); } console.log("No Cached value found for "+val+ " Querying database..."); return new Promise((resolve, reject) => { connection.query(`SELECT * from twitter.Users where id IN (SELECT follower_id from twitter.User_Follower WHERE user_id = ${ownerId})`, (err, rows, fields) => { if (err) reject(err); console.log("ROWS "+rows); let followers = []; if(rows.length > 0) { rows.forEach(user => { followers.push(user); }); redisConnection.set(`OwnerCache_${ownerId}`, JSON.stringify(followers)); redisConnection.expire(`OwnerCache_${ownerId}`, 10); resolve(followers); } }); }); });
错误信息
使用桥接网络名称mysql-db可正常连接MySQL,但使用redis-db却无法连接Redis(本地运行时Node可连接两者,此时默认使用localhost)。
Listening on port 3000 Returning all the followers for ownerID 1 ################ mysql-db ################ redis-db node:internal/process/promises:288 triggerUncaughtException(err, true /* fromPromise */); ^ Error: connect ECONNREFUSED 127.0.0.1:6379 at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1246:16) Emitted 'error' event on Commander instance at: at RedisSocket.<anonymous> (/app/node_modules/@node-redis/client/dist/lib/client/index.js:339:14) at RedisSocket.emit (node:events:513:28) at RedisSocket._RedisSocket_connect (/app/node_modules/@node-redis/client/dist/lib/client/socket.js:117:14) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async Commander.connect (/app/node_modules/@node-redis/client/dist/lib/client/index.js:162:9) { errno: -111, code: 'ECONNREFUSED', syscall: 'connect', address: '127.0.0.1', port: 6379 } Node.js v18.6.0
解决思路
修正Redis客户端参数格式
你使用的是@node-redis/client(v4+版本),该版本不再支持将host和port作为顶级参数传入createClient,需要嵌套在socket对象中,或者使用URL格式:// 方式1:使用socket对象 redisConnection = redis.createClient({ socket: { host: process.env.REDIS_HOST || 'localhost', port: 6379 } }); // 方式2:使用URL格式 redisConnection = redis.createClient({ url: `redis://${process.env.REDIS_HOST || 'localhost'}:6379` });确保Redis服务就绪后再连接
depends_on仅保证容器启动顺序,不保证Redis服务完全就绪。可以在代码中添加重试逻辑:async function connectRedis() { try { await redisConnection.connect(); console.log('Redis连接成功'); } catch (err) { console.log('Redis连接失败,2秒后重试...'); setTimeout(connectRedis, 2000); } } // 替换原redisConnection.connect()为connectRedis()验证容器间网络连通性
进入express-app容器,执行以下命令测试与Redis容器的网络连通性:docker exec -it <express-app容器ID> ping redis-db docker exec -it <express-app容器ID> telnet redis-db 6379如果telnet不通,查看Redis容器日志排查启动问题:
docker logs redis-db检查Redis挂载目录权限
Redis容器默认使用redis用户运行,若挂载目录权限不正确会导致服务启动失败。可以在docker-compose.yml中显式指定用户:redis-db: image: redis ports: - '6379:6379' volumes: - my-twitter-redis-mount:/var/lib/redis/data command: redis-server --appendonly yes user: "redis"
内容的提问来源于stack exchange,提问作者Frederick Scott Smith
相关产品推荐
相关产品推荐

