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
二、排查过程
- 初步排查 - 密码配置问题
-
检查 application.yaml 发现密码包含特殊字符 +
-
尝试用引号包裹密码:password: "xx"
-
结果:无效,问题依旧
- 手动配置 RedisConnectionFactory
-
创建 RedisConfig.java 手动配置 LettuceConnectionFactory
-
硬编码密码测试
-
结果:无效,问题依旧
- DNS 解析排查
-
错误日志显示 <unresolved> 表示 DNS 未解析
-
使用 nslookup 命令验证 DNS 正常解析到 xx
-
尝试直接使用 IP 地址连接
-
尝试配置 Lettuce 使用 JDK DNS Resolver
-
结果:无效,问题依旧
- Socket 直连测试
-
编写 Java Socket 代码直接连接 Redis 发送 AUTH/PING 命令
-
结果:
Socket连接成功
AUTH响应: +OK
PING响应: +PONG
- 结论:密码正确、网络正常、DNS 正常
- 替换为 Jedis
-
排除 Lettuce,改用 Jedis
-
结果:✅ 连接成功
- 对比分析正常项目
-
查看 项目B(能正常使用 Lettuce)
-
发现关键配置差异:
ClientOptions clientOptions = ClientOptions.builder()
.protocolVersion(ProtocolVersion.RESP2)
.build();
- Redis 版本确认
-
Socket 发送 INFO server 命令获取版本
-
结果:redis_version:5.0
- 最终定位
-
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 行
七、关键经验总结
-
错误信息 <unresolved> 不一定是 DNS 问题 - 可能是协议层错误导致的副作用
-
Socket 测试能排除网络、密码、DNS 问题 - 简单有效的诊断手段
-
对比正常项目配置 - 快速发现关键差异
-
Lettuce 6.x + Redis 5.x 组合需要强制 RESP2 - 重要兼容性问题