无法连接Azure Redis服务问题排查求助
Azure Redis缓存连接失败(ECONNRESET)问题排查与解决
问题背景
在Azure部署Web应用并配置Azure Redis缓存后,缓存无法正常工作。Next.js前端托管在Vercel平台,本地连接localhost Redis正常,但连接Azure Redis服务时请求陷入无限循环,最终出现CORS错误(已开启Azure CORS),仅涉及Redis的请求出现此问题。
现有代码与配置
Redis客户端代码
const redis = require("redis"); const dotenv = require('dotenv'); dotenv.config({ path: './.env' }); // 缓存环境变量 const cacheHostName = process.env.AZURE_CACHE_FOR_REDIS_HOST_NAME; const cachePassword = process.env.AZURE_CACHE_FOR_REDIS_ACCESS_KEY; if (!cacheHostName) throw new Error("AZURE_CACHE_FOR_REDIS_HOST_NAME is empty"); if (!cachePassword) throw new Error("AZURE_CACHE_FOR_REDIS_ACCESS_KEY is empty"); exports.connect = async () => { let client; if (process.env.NODE_ENV === 'production') { client = redis.createClient({ // 生产环境Redis TLS配置 url: `redis://${cachePassword}@${cacheHostName}:6380`, tls: { rejectUnauthorized: true // 确保启用SSL验证 } }); } else { // 本地测试Redis配置 client = redis.createClient({ host: 'localhost', port: 6379 }); } client.on('connect', () => { console.log('Redis客户端已连接到服务器'); }); client.on('error', (err) => { console.error('Redis客户端连接服务器失败:', err); }); client.on('end', () => { console.log('Redis客户端连接已关闭'); }); await client.connect(); return client; };
控制器中Redis使用代码
const { User } = require('../models'); const asyncHandler = require('../middlewares/async'); const { connect } = require('../redis/redis') //@route GET api/users/ //@desc 获取所有用户 //@access 私有 exports.getUsers = asyncHandler(async (req, res, next) => { const redis = await connect(); const key = 'users'; const redisUsers = await redis.get(key); if( redisUsers ) { console.log('从Redis获取用户') return res.status(200).json({ success: true, data: JSON.parse(redisUsers) }) } console.log('从数据库获取用户') const users = await User.findAll(); await redis.set(key, JSON.stringify(users)); return res.status(200).json({ success: true, data: users }) });
环境配置矛盾点
- Redis服务SSL端口已禁用,但代码中使用了SSL默认端口6380
- TLS版本设置为1.2
错误日志
Redis客户端已连接到服务器 Redis客户端连接服务器失败: Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:218:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } Redis客户端连接服务器失败: Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:218:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } Redis客户端已连接到服务器 Redis客户端连接服务器失败: Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:218:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } Redis客户端连接服务器失败: Error: read ECONNRESET at TCP.onStreamRead (node:internal/stream_base_commons:218:20) { errno: -4077, code: 'ECONNRESET', syscall: 'read' } Redis客户端已连接到服务器
核心问题分析
- 端口与SSL配置不匹配:代码使用6380(SSL端口)但Azure Redis已禁用SSL,应使用非SSL端口6379并移除
tls配置 - 每次请求创建新连接:控制器中每次调用
getUsers都新建Redis连接,导致连接频繁创建/断开,引发ECONNRESET - URL格式错误:Azure Redis非SSL连接的URL需在密码前添加冒号,格式应为
redis://:<password>@<hostname>:6379
解决方案步骤
1. 修正Redis客户端连接配置
根据Azure Redis的SSL禁用状态,修改生产环境连接逻辑:
if (process.env.NODE_ENV === 'production') { // 禁用SSL时使用非SSL端口6379,移除tls配置 client = redis.createClient({ url: `redis://:${cachePassword}@${cacheHostName}:6379` }); }
若后续重新启用SSL,需改回6380并添加TLS配置:
client = redis.createClient({ url: `rediss://:${cachePassword}@${cacheHostName}:6380`, // 使用rediss协议 tls: { minVersion: 'TLSv1.2' // 匹配Azure要求的TLS版本 } });
2. 复用Redis连接(关键优化)
避免每次请求创建新连接,改为全局初始化一次连接:
- 修改
redis/redis.js实现全局客户端:
const redis = require("redis"); const dotenv = require('dotenv'); dotenv.config({ path: './.env' }); const cacheHostName = process.env.AZURE_CACHE_FOR_REDIS_HOST_NAME; const cachePassword = process.env.AZURE_CACHE_FOR_REDIS_ACCESS_KEY; if (!cacheHostName) throw new Error("AZURE_CACHE_FOR_REDIS_HOST_NAME is empty"); if (!cachePassword) throw new Error("AZURE_CACHE_FOR_REDIS_ACCESS_KEY is empty"); // 全局客户端实例 let client; async function initClient() { if (client) return client; if (process.env.NODE_ENV === 'production') { client = redis.createClient({ url: `redis://:${cachePassword}@${cacheHostName}:6379` }); } else { client = redis.createClient({ host: 'localhost', port: 6379 }); } client.on('connect', () => { console.log('Redis客户端已连接到服务器'); }); client.on('error', (err) => { console.error('Redis客户端连接服务器失败:', err); }); client.on('end', () => { console.log('Redis客户端连接已关闭'); }); await client.connect(); return client; } // 初始化并导出客户端 const redisClient = initClient(); module.exports = { getClient: () => redisClient };
- 修改控制器代码复用全局连接:
const { User } = require('../models'); const asyncHandler = require('../middlewares/async'); const { getClient } = require('../redis/redis') exports.getUsers = asyncHandler(async (req, res, next) => { const redis = await getClient(); const key = 'users'; const redisUsers = await redis.get(key); if( redisUsers ) { console.log('从Redis获取用户') return res.status(200).json({ success: true, data: JSON.parse(redisUsers) }) } console.log('从数据库获取用户') const users = await User.findAll(); await redis.set(key, JSON.stringify(users), { EX: 3600 }); // 添加缓存过期时间 return res.status(200).json({ success: true, data: users }) });
3. 验证Azure Redis网络配置
- 检查Azure Redis的防火墙规则,确保Web应用的出站IP已加入允许列表
- 确认Redis实例的非SSL端口(6379)已启用(在Azure门户Redis实例设置中查看)
4. 调试与验证
- 在Azure门户的Redis控制台执行
PING命令,确认服务正常 - 监控Web应用日志,确保Redis连接不再频繁断开
内容的提问来源于stack exchange,提问作者mateo ghidini
相关产品推荐
相关产品推荐

