高效排查常见Redis连接错误
遇到Redis连接问题?本实用指南提供清晰步骤,诊断并解决'连接被拒绝'、'超时'和'认证失败'等常见错误。学习检查服务器状态、网络配置、防火墙及Redis性能指标。包含`redis-cli`和客户端库的可操作示例,助您快速恢复Redis连接。
高效排查常见Redis连接错误
Redis连接错误通常很简单,一旦你将它们分解为三个问题:客户端能否到达主机和端口,Redis是否接受连接,以及连接后客户端是否被允许执行命令。
按顺序进行。当Redis停止时,直接跳入应用程序代码会浪费时间。当密码错误时,重建防火墙规则会浪费时间。一个小的、可重复的检查清单能让你更快地找到真正的故障。
首先,从与应用程序相同的位置进行测试
从你的笔记本电脑测试很有用,但这并不能证明Kubernetes Pod、VM、容器或CI运行器可以到达Redis。从与故障应用程序相同的网络位置开始。
redis-cli -h redis.example.internal -p 6379 PING
预期输出:
PONG
如果Redis需要TLS,请使用你的部署期望的TLS选项:
redis-cli --tls -h redis.example.internal -p 6380 PING
如果Redis需要认证:
redis-cli -u redis://app-user:[email protected]:6379 PING
注意shell历史记录中的密码。对于生产环境,尽可能使用临时凭据或环境变量。
连接被拒绝
ECONNREFUSED、Connection refused或Could not connect to Redis通常意味着TCP连接到达了目标主机,但该端口上没有东西接受它。最常见的原因很简单:
- Redis未运行。
- 客户端使用了错误的主机或端口。
- Redis仅绑定到localhost。
- 容器或服务映射指向了错误的端口。
- 防火墙主动拒绝了连接。
在Redis主机上,检查进程和监听器:
redis-cli PING
ps aux | grep '[r]edis-server'
ss -ltnp | grep redis
你希望看到Redis在预期的地址和端口上监听,通常是127.0.0.1:6379、0.0.0.0:6379或私有接口地址。
检查redis.conf或有效配置:
redis-cli CONFIG GET bind
redis-cli CONFIG GET port
redis-cli CONFIG GET protected-mode
如果bind是127.0.0.1,远程客户端无法直接连接。这通常是故意的。除非Redis受到认证、ACL、防火墙规则和私有网络的保护,否则不要将其更改为0.0.0.0作为快速修复。Redis暴露在公共互联网上是一个严重的安全事件。
在Docker中,记住容器端口和主机端口之间的区别:
docker ps
docker port <redis-container>
在Docker Compose网络内部,应用程序通常连接到服务名称和内部端口:
redis://redis:6379
从主机,它们可能连接到已发布的端口,例如localhost:6379或localhost:6381,具体取决于映射。
连接超时
超时意味着客户端等待但未及时完成操作。与拒绝连接不同,超时通常指向路径问题或服务器繁忙。
检查TCP路径:
nc -vz redis.example.internal 6379
ping -c 5 redis.example.internal
ping并不完美,因为ICMP可能被阻止而TCP正常工作,但它可以揭示明显的DNS或路由错误。nc更接近Redis客户端的需求。
如果TCP连接成功但Redis命令超时,检查Redis是否繁忙:
redis-cli INFO clients
redis-cli INFO stats
redis-cli INFO memory
redis-cli SLOWLOG GET 10
redis-cli LATENCY DOCTOR
查找阻塞的客户端、高连接数、接近maxmemory的内存、主机上的交换、慢命令和延迟事件。单个昂贵的命令,如KEYS *、大型HGETALL或长Lua脚本,可能会延迟不相关的客户端,因为Redis命令执行基本上是单线程的。
同时检查客户端超时设置。一些库对连接超时或命令超时使用较短的默认值。在慢速网络上增加超时可能会减少误报,但不应掩盖过载的Redis实例。如果从应用程序主机执行简单的PING需要几秒钟,请先修复此问题,然后再调整重试。
名称解析和错误端点问题
并非每个连接错误都是Redis引起的。DNS和服务发现也会导致很多问题。
从应用程序主机:
getent hosts redis.example.internal
nslookup redis.example.internal
在Kubernetes中:
kubectl exec -it deploy/my-app -- sh
getent hosts redis.default.svc.cluster.local
nc -vz redis.default.svc.cluster.local 6379
检查应用程序是否使用了只读副本端点、哨兵端点、集群端点或直接节点端点。Redis Cluster客户端需要集群感知库,因为键可能属于不同的槽,命令可能会收到重定向。非集群感知的客户端可能会成功连接,然后在发送实际命令时出现MOVED或ASK错误。
认证错误
认证失败表现为:
NOAUTH Authentication requiredWRONGPASS invalid username-password pairNOPERM this user has no permissions- 客户端库特定的认证异常
对于Redis 6及更高版本,ACL用户很常见。连接字符串可能需要用户名和密码:
redis://app-user:[email protected]:6379/0
对于默认用户,某些客户端仅使用密码:
redis://:[email protected]:6379/0
如果你有管理员访问权限,请检查活动用户配置:
redis-cli ACL LIST
redis-cli ACL GETUSER app-user
NOAUTH表示客户端在发出命令之前未进行身份验证。WRONGPASS表示尝试了身份验证但被拒绝。NOPERM表示身份验证成功,但用户没有该命令、键模式或Pub/Sub通道的权限。
当密钥轮换时,确认每个正在运行的进程确实收到了新值。在容器平台上,更新密钥对象并不总是会重启现有的Pod或进程。一个常见的现实故障是,一半的应用程序使用新密码,另一半仍在使用旧密码。
TLS不匹配
TLS错误可能看起来像连接重置、超时或不可读的协议错误。
检查端口。托管服务通常对TLS和非TLS Redis使用不同的端口。例如,一个端点可能期望纯Redis协议,另一个可能期望从第一个字节开始使用TLS。
测试:
redis-cli --tls -h redis.example.internal -p 6380 PING
redis-cli -h redis.example.internal -p 6379 PING
如果你的组织使用私有证书,客户端可能还需要CA文件:
redis-cli --tls --cacert /path/to/ca.pem -h redis.example.internal -p 6380 PING
在应用程序日志中,证书错误通常比顶层的Redis异常更清晰。查找关于未知证书颁发机构、过期证书、主机名不匹配或握手失败的消息。
连接过多
Redis有maxclients限制。操作系统也有文件描述符限制。当任一限制耗尽时,新客户端可能失败或现有客户端可能表现不佳。
检查:
redis-cli INFO clients
redis-cli CONFIG GET maxclients
ulimit -n
有用的字段包括connected_clients、blocked_clients和来自INFO stats的rejected_connections。
连接过多通常来自以下模式之一:
- 每个Web请求创建一个新的Redis客户端。
- 在短生命周期的作业中不关闭客户端。
- 工作进程过多,每个都有自己的大连接池。
- Pub/Sub订阅从普通命令池借用连接。
- Redis重启期间的重试风暴。
在提高限制之前修复应用程序形态。每个进程使用一个共享客户端或有界连接池。添加带有抖动的重试退避,以便在中断后所有实例不会在同一毫秒内重新连接。
保护模式和绑定设置
Redis保护模式旨在减少意外暴露造成的损害。如果Redis绑定广泛且没有认证,保护模式可能会拒绝远程连接。
检查:
redis-cli CONFIG GET protected-mode
redis-cli CONFIG GET bind
redis-cli CONFIG GET requirepass
不要仅仅为了让远程连接工作而禁用保护模式。更安全的路径通常是私有网络加上认证和狭窄的绑定地址。如果Redis必须接受远程客户端,请将其放在私有子网上,限制源IP,要求凭据,并在适当时使用TLS。
实用的操作顺序
当应用程序无法连接时,请使用此顺序:
- 从应用程序环境中,针对相同的主机和端口运行
redis-cli PING。 - 如果被拒绝,检查Redis进程、监听器、绑定、端口和容器映射。
- 如果超时,检查路由、防火墙规则、服务器负载、慢命令和客户端超时设置。
- 如果认证失败,验证用户名、密码、ACL权限和密钥部署。
- 如果只有某些命令失败,检查ACL命令/键权限和Redis Cluster重定向。
- 如果在负载下失败,检查连接数、连接池大小、重试和服务器资源指标。
连接故障排除主要是收集证据。从与应用程序相同的位置获得干净的CLI结果,然后将其与客户端库正在执行的操作进行比较。一旦这两个路径出现差异,差距通常是可见的:缺少TLS标志、旧密码、错误的服务名称或创建了远超Redis处理能力的连接池。
读取应用程序错误而不反应过度
客户端库将Redis错误包装在自己的语言中。Node.js服务可能显示ECONNRESET,Python工作进程可能显示redis.exceptions.ConnectionError,Java服务可能报告连接池获取超时。这些都可能描述同一问题的不同层面。
区分它们:
- 连接超时:TCP连接未及时完成。
- 读取超时:连接存在,但命令响应未及时到达。
- 连接重置:连接被Redis、代理、网络或对端关闭。
- 连接池超时:应用程序无法从其自己的连接池中借出Redis连接。
- 认证错误:Redis拒绝了凭据或权限。
连接池超时很容易被误读为Redis宕机。有时Redis正常,但应用程序借用了所有连接池连接且从未归还。Pub/Sub滥用可能导致此问题。长时间阻塞的命令、忘记关闭客户端的请求处理程序或对于进程并发性来说太小的连接池也可能导致此问题。
同时检查双方。在应用程序中,如果库暴露了连接池指标,请检查:活动连接、空闲连接、等待者、重试次数。在Redis中,检查:
redis-cli INFO clients
redis-cli CLIENT LIST | head
如果Redis只显示少量客户端,但应用程序说其连接池已耗尽,则问题可能出在应用程序进程内部。如果Redis显示来自同一部署的数千个连接,则服务可能过于频繁地创建客户端。
重试值得特别关注。没有退避的重连循环可能会将短暂的Redis重启变成一场风暴。每个应用程序实例立即尝试重新连接,认证和TLS握手激增,Redis必须在被客户端猛攻的同时恢复。使用指数退避和抖动。还要决定哪些命令可以安全重试。重试幂等的缓存GET与重试可能在连接断开之前已经成功的写入不同。
对于事件记录,捕获确切的错误文本和时间。“Redis宕机”通常是错误的。“从14:03到14:06 UTC,应用Pod看到读取超时,而Redis CPU使用一个核心,SLOWLOG显示大型HGETALL调用”是可操作的。