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 - 重要兼容性问题

相关推荐
high201110 小时前
【架构】-- Mysql delete vs truncate 深度解析
数据库·mysql·架构
Hotakus10 小时前
[开源] 关于我给OpenCode弄了个缓存统计插件这件事 OpenCode Visual Cache
缓存·typescript·开源软件
l1t10 小时前
DeepSeek总结的DuckDB CLAUDE.md
数据库·人工智能
蜜獾云10 小时前
Redis常用集群以及性能压测实战
数据库·redis·缓存
fengxin_rou10 小时前
【Redis 位图分片计数详解】:原理、实战架构与避坑最佳实践
数据库·redis·架构·bitmap
ZC跨境爬虫10 小时前
跟着 MDN 学 HTML day_63:(Web 中矢量图形的完整指南)
前端·javascript·数据库·ui·html
靠谱品牌推荐官10 小时前
【高并发实战】如何基于缓存穿透治理机制设计一套高可用的小程序本地缓存中台架构?
缓存·小程序·架构
历程里程碑10 小时前
53 多路转接select
linux·开发语言·数据结构·数据库·c++·sql·排序算法
闪电悠米10 小时前
黑马点评短信登录02_redis_token_login
数据库·redis·firefox