0%

Redis远程主机强迫关闭了一个现有的连接

现象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
025-11-07 13:58:40.051 ERROR [celina-data-center,,] 34880 --- [isson-netty-1-6] o.redisson.client.handler.CommandsQueue  : Exception occured. Channel: [id: 0xdd6f0878, L:/192.168.10.182:60652 - R:dmz-zjj.lanniuh.com/172.16.16.33:7073]

java.io.IOException: 远程主机强迫关闭了一个现有的连接。
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:192)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:379)
at io.netty.buffer.PooledByteBuf.setBytes(PooledByteBuf.java:258)
at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1132)
at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:350)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:151)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:750)

日志中出现的 java.io.IOException: 远程主机强迫关闭了一个现有的连接 错误,通常意味着 Redisson 客户端与 Redis 服务器之间的网络连接被远程端(Redis 服务器或中间网络设备)主动关闭。以下是具体原因分析和解决方法:

一、核心原因分析

该错误的本质是:Redisson 客户端与 Redis 服务器的 TCP 连接被强行终止(非正常断开),可能的触发点包括:

  1. Redis 服务器主动关闭连接
    • Redis 配置了 timeout 参数(默认 0,即永不超时),若设置了非零值(如 300 秒),当连接空闲时间超过该值时,服务器会主动关闭连接。
    • Redis 服务器内存不足、重启、宕机或被杀死进程,导致连接中断。
  2. 中间网络设备干预
    • 防火墙、负载均衡器(如 Nginx、F5)或网关配置了连接超时策略,当连接空闲时间超过阈值(如 60 秒),会主动切断连接。
    • 网络波动、丢包严重,导致 TCP 连接被底层协议判定为无效而关闭。
  3. Redisson 客户端配置不当
    • 客户端未配置心跳检测,无法维持长连接,被服务器或网络设备判定为 “死连接” 而关闭。
    • 客户端与服务器之间的 keepalive 机制未启用,TCP 层无法检测连接有效性。

二、解决方法(按优先级排序)

1. 检查并调整 Redis 服务器的超时配置

  • 登录 Redis 服务器,执行以下命令查看 timeout 配置:

    bash

    1
    redis-cli config get timeout
  • 若 timeout 不为 0(如 300 秒),建议临时改为 0(关闭超时),观察是否还会出现连接关闭:

    bash

    1
    redis-cli config set timeout 0
    • 若需长期生效,修改 Redis 配置文件(redis.conf):

      conf

      1
      timeout 0  # 禁用连接超时(默认值)
    • 注意:timeout 0 表示 Redis 不会主动关闭空闲连接,适合长连接场景(如 Redisson 客户端)。

2. 排查中间网络设备的超时策略

  • 联系运维团队,检查防火墙、负载均衡器、网关的配置:
    • 例如,Nginx 反向代理 Redis 时,若配置了 proxy_timeout 60s,会导致 60 秒空闲后断开连接,需延长该值(如 3600s)或禁用。
    • 云服务器的安全组、网络 ACL 可能有类似的 “连接超时” 限制,需调整为适合业务的时长(建议至少 300 秒以上)。
  • 若无法修改网络设备配置,可通过客户端主动发送心跳维持连接(见步骤 3)。

3. 配置 Redisson 客户端的心跳与重连机制

Redisson 提供了连接检测和自动重连功能,需在客户端配置中启用:

java

运行

1
2
3
4
5
6
7
8
9
10
// Redisson 配置示例(Spring Boot 可在 application.yml 中配置)
Config config = new Config();
config.useSingleServer()
.setAddress("redis://dmz-zjj.lanniuh.com:7073") // Redis 地址
.setConnectionMinimumIdleSize(5) // 保持最小空闲连接数,避免频繁创建/关闭
.setIdleConnectionTimeout(10000) // 空闲连接超时时间(毫秒),建议小于网络超时
.setPingConnectionInterval(30000) // 定期发送 PING 心跳(毫秒),如 30 秒一次,防止连接被判定为空闲
.setReconnectionTimeout(3000) // 重连超时时间
.setRetryAttempts(3) // 重试次数
.setRetryInterval(1000); // 重试间隔(毫秒)
  • 关键参数说明
    • pingConnectionInterval:定期发送 PING 命令(如 30 秒一次),确保连接被服务器和网络设备视为 “活跃”,避免被超时关闭。
    • idleConnectionTimeout:客户端主动关闭空闲超过该时间的连接,需小于网络设备的超时时间(如网络超时 60 秒,则设为 50 秒)。

4. 启用 TCP 层的 keepalive 机制

通过操作系统配置 TCP 保活机制,底层检测并维持连接:

  • Linux 服务器(客户端所在机器)

    bash

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 临时生效
    echo 1 > /proc/sys/net/ipv4/tcp_keepalive_time # 连接空闲多久后发送第一个保活包(秒,默认 7200)
    echo 5 > /proc/sys/net/ipv4/tcp_keepalive_intvl # 保活包重发间隔(秒,默认 75)
    echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes # 未收到响应时,重试发送保活包的次数(默认 9)

    # 长期生效(修改 /etc/sysctl.conf)
    net.ipv4.tcp_keepalive_time = 600
    net.ipv4.tcp_keepalive_intvl = 10
    net.ipv4.tcp_keepalive_probes = 3
    # 生效配置
    sysctl -p

    建议将 tcp_keepalive_time 设为 600 秒(10 分钟),确保在网络超时前触发保活检测。

5. 检查 Redis 服务器稳定性

  • 查看 Redis 服务器日志(通常在 redis.conf 中配置的 logfile 路径),确认是否有内存溢出(OOM)、崩溃重启等记录。

  • 监控 Redis 服务器的资源使用:

    bash

    1
    2
    3
    4
    # 查看内存使用
    redis-cli info memory | grep used_memory_human
    # 查看连接数
    redis-cli info clients | grep connected_clients

    若服务器频繁重启或内存不足,需扩容资源或优化 Redis 内存配置(如 maxmemory-policy)。

三、临时排查手段

  1. 抓包分析:在客户端机器使用 tcpdump 抓取与 Redis 服务器的通信包,确认连接关闭的发起方(客户端 / 服务器 / 中间设备):

    bash

    1
    tcpdump -i any host 172.16.16.33 and port 7073 -w redis_packet.pcap

    通过 Wireshark 分析 pcap 文件,若看到 FIN 或 RST 包来自服务器 IP,则说明服务器主动关闭。

  2. 测试连接稳定性:使用 telnet 或 nc 模拟长连接,观察是否会被断开:

    bash

    1
    2
    telnet 172.16.16.33 7073
    # 保持连接,观察是否会自动断开

    若一段时间后断开,说明网络层或服务器有超时策略。

总结

该错误的核心解决思路是:通过心跳机制维持连接活跃性 + 调整服务器 / 网络的超时配置 + 启用自动重连。优先检查 Redis 的 timeout 配置和网络设备的超时策略,再通过 Redisson 的 pingConnectionInterval 发送心跳,基本可解决大部分 “远程主机关闭连接” 问题。