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

无法连接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客户端已连接到服务器

核心问题分析

  1. 端口与SSL配置不匹配:代码使用6380(SSL端口)但Azure Redis已禁用SSL,应使用非SSL端口6379并移除tls配置
  2. 每次请求创建新连接:控制器中每次调用getUsers都新建Redis连接,导致连接频繁创建/断开,引发ECONNRESET
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 22:40:58