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

相关推荐
这个DBA有点耶1 天前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
DBA_G1 天前
从地面到云霄:GBase数据库在民航三大场景的落地实践
数据库
自由能燃气设备2 天前
商用全预混低氮冷凝锅炉免费方案vs付费方案对比+选型避坑指南
大数据·数据库·人工智能
科创致远2 天前
科创致远 ESOP 系统核心效能与实战价值展示
大数据·数据库·人工智能·精益工程
IT大白鼠2 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz2 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
2601_962218612 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法
全栈弄潮儿²⁰²⁴2 天前
AI Agent 开发实战(30):限流、缓存与成本控制
人工智能·gpt·缓存·agent·限流·agi·成本控制
张洛闻Eren2 天前
k8s云原生【第十课】:水平 Pod 自动扩缩容
运维·数据库·云原生·kubernetes·github
于平安2 天前
MySQL-触发器
数据库·mysql