Redis 连接问题完整解决报告

Redis 连接问题完整解决报告

一、问题现象

Spring Boot 3.4.0 应用启动后,调用 /api/agent/chat 接口时报错:

org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis

Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to xxxxxxx/<unresolved>:6379

Caused by: io.lettuce.core.RedisCommandExecutionException: NOAUTH Authentication required

二、排查过程

  1. 初步排查 - 密码配置问题
  • 检查 application.yaml 发现密码包含特殊字符 +

  • 尝试用引号包裹密码:password: "xx"

  • 结果:无效,问题依旧

  1. 手动配置 RedisConnectionFactory
  • 创建 RedisConfig.java 手动配置 LettuceConnectionFactory

  • 硬编码密码测试

  • 结果:无效,问题依旧

  1. DNS 解析排查
  • 错误日志显示 <unresolved> 表示 DNS 未解析

  • 使用 nslookup 命令验证 DNS 正常解析到 xx

  • 尝试直接使用 IP 地址连接

  • 尝试配置 Lettuce 使用 JDK DNS Resolver

  • 结果:无效,问题依旧

  1. Socket 直连测试
  • 编写 Java Socket 代码直接连接 Redis 发送 AUTH/PING 命令

  • 结果:

Socket连接成功

AUTH响应: +OK

PING响应: +PONG

  • 结论:密码正确、网络正常、DNS 正常
  1. 替换为 Jedis
  • 排除 Lettuce,改用 Jedis

  • 结果:✅ 连接成功

  1. 对比分析正常项目
  • 查看 项目B(能正常使用 Lettuce)

  • 发现关键配置差异:

ClientOptions clientOptions = ClientOptions.builder()

.protocolVersion(ProtocolVersion.RESP2)

.build();

  1. Redis 版本确认
  • Socket 发送 INFO server 命令获取版本

  • 结果:redis_version:5.0

  1. 最终定位
  • Lettuce 6.x 默认使用 RESP3 协议

  • Redis 5.0 仅支持 RESP2 协议

  • 协议版本不匹配导致认证失败

三、根本原因

┌──────────────────┬──────────┬────────────────┐

│ 组件 │ 默认协议 │ 问题 │

├──────────────────┼──────────┼────────────────┤

│ Lettuce 6.x │ RESP3 │ 默认使用 RESP3 │

├──────────────────┼──────────┼────────────────┤

│ 阿里云 Redis 5.0 │ RESP2 │ 不支持 RESP3 │

└──────────────────┴──────────┴────────────────┘

RESP3 协议在 Redis 6.0+ 才引入,Redis 5.0 只支持 RESP2。Lettuce 用 RESP3 连接 Redis 5.0 时,协议握手失败,导致后续 AUTH 命令格式不正确。

四、解决方案

方案一:强制使用 RESP2 协议(推荐)

@Configuration

public class RedisConfig {

@Value("${spring.data.redis.host}")

private String host;

@Value("${spring.data.redis.port}")

private int port;

@Value("${spring.data.redis.password}")

private String password;

@Value("${spring.data.redis.database}")

private int database;

@Bean

@Primary

public LettuceConnectionFactory redisConnectionFactory() {

RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();

config.setHostName(host);

config.setPort(port);

config.setPassword(RedisPassword.of(password));

config.setDatabase(database);

// 关键配置:强制使用 RESP2 协议

ClientOptions clientOptions = ClientOptions.builder()

.protocolVersion(ProtocolVersion.RESP2)

.build();

LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()

.clientOptions(clientOptions)

.build();

LettuceConnectionFactory factory = new LettuceConnectionFactory(config, clientConfig);

factory.setShareNativeConnection(false);

factory.afterPropertiesSet();

return factory;

}

@Bean

public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {

RedisTemplate<String, Object> template = new RedisTemplate<>();

template.setConnectionFactory(connectionFactory);

template.setKeySerializer(new StringRedisSerializer());

template.setValueSerializer(new StringRedisSerializer());

return template;

}

@Bean

public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {

return new StringRedisTemplate(connectionFactory);

}

}

五、RESP 协议版本对照表

┌─────────────────┬──────────────┬──────────────────┐

│ Redis 版本 │ 支持协议 │ 说明 │

├─────────────────┼──────────────┼──────────────────┤

│ Redis 2.x - 5.x │ RESP2 │ 仅支持 RESP2 │

├─────────────────┼──────────────┼──────────────────┤

│ Redis 6.0+ │ RESP2, RESP3 │ 同时支持两种协议 │

└─────────────────┴──────────────┴──────────────────┘

┌──────────────┬──────────┬────────────────────────────┐

│ Lettuce 版本 │ 默认协议 │ 说明 │

├──────────────┼──────────┼────────────────────────────┤

│ Lettuce 5.x │ RESP2 │ 默认 RESP2 │

├──────────────┼──────────┼────────────────────────────┤

│ Lettuce 6.x │ RESP3 │ 默认 RESP3,可配置为 RESP2 │

└──────────────┴──────────┴────────────────────────────┘

六、验证 Redis 版本的方法

方法1:redis-cli

redis-cli -h <host> -p <port> -a <password> INFO server

方法2:Java Socket

Socket socket = new Socket(host, port);

PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

out.println("AUTH " + password);

in.readLine();

out.println("INFO server");

// 解析输出中的 redis_version 行

七、关键经验总结

  1. 错误信息 <unresolved> 不一定是 DNS 问题 - 可能是协议层错误导致的副作用

  2. Socket 测试能排除网络、密码、DNS 问题 - 简单有效的诊断手段

  3. 对比正常项目配置 - 快速发现关键差异

  4. Lettuce 6.x + Redis 5.x 组合需要强制 RESP2 - 重要兼容性问题

相关推荐
用户30745969820715 小时前
Redis 延时队列详解
redis
GBASE15 小时前
G术时刻 |GBase 8s数据库事务并发控制之封锁技术介绍(下)
数据库
烤代码的吐司君18 小时前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
xiezhr1 天前
逛GitHub发现了一款免费的带AI功能的数据库管理工具
数据库·ai编程·dba
吃糖的小孩2 天前
给 QQ AI 机器人设计“可控记忆”:会话摘要、手动长期记忆与角色卡边界
数据库
笃行3503 天前
金仓数据库数据安全双防线:静态存储加密与传输加密实战
数据库
笃行3503 天前
金仓数据库物理备份实战:sys_rman 全流程演练与误覆盖抢救
数据库
笃行3503 天前
金仓数据库逻辑备份实战:从全库导出到 Schema 替换的完整闭环
数据库
leeyi3 天前
Checkpoint 机制:Agent 怎么在断电后接着跑
redis·aigc·agent