redisson简单使用--内存溢出异常:o.redisson.client.handler.CommandsQueue [153] : Exception

4,191 阅读3分钟

我这里用的版本信息为:redisson-spring-boot-starter 版本:3.11.4

异常信息:

2021-08-31 09:46:47.101 ERROR [sson-netty-2-27] [] o.redisson.client.handler.CommandsQueue  [153] : Exception occured. Channel: [id: 0x1b06042a, L:/192.168.100.54:56055 - R:192.168.104.101/192.168.104.101:6379]

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:378)
	at io.netty.buffer.PooledByteBuf.setBytes(PooledByteBuf.java:253)
	at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:1133)
	at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:350)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:148)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:714)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:650)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:576)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
	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:748)

搜了半天,搜到github下面有人提了这个问题,但是已被关闭了,这时候还没有意识到可能是我使用的版本太低了导致的。

首先看下异常信息的提示:

  1. 提示通道异常:Exception occured. Channel,然后是IO异常:java.io.IOException,首先涉及到IO,就有可能是内存读写问题,也有可能是客户端连接异常,没有拿到正常的redis连接。

翻了半天没找到解决办法,这时看到上面的issues了,并且是18年的,应该考虑是否是版本问题,但是第一次用这玩意,我也没有往这方面想,直接找了一个历史版本就扔进去了。

接着切换了 redisson 的版本到:3.13.6,目前最新版已是:3.16.1 了,见: https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter

image.png 由于我这里springboot版本为:2.2.5.RELEASE,最新的都已经是:2.5.4了,所以就没敢上太新的,避免再次因为版本问题出现奇奇怪怪的异常了。

最后升级完redisson版本后,翻到一篇博客提到是内存异常,貌似跟我这个问题沾点边:redisson内存溢出排查

虽然没看懂大佬是这么调试的源码,但是看到这个就很像上面的异常信息:

image.png

这里附上大佬的分析:

前通过升级 redisson->3.12.5+的版本,解决了此问题,通过分析,最终原因如下
1. Queue的take会导致当前的连接被长时间阻塞住,会导致当前连接的探活会一直按照定时任务新建CommandData,但是由于没法通信 导致对象一直在队列中排队,无限增长,导致最终OOM,只有ping发出去了 那个里面的command对象会被清理掉,等到下次连接复用的时候再new Command 这样才不会OOM
2. 小于3.12.5的redisson是不会判断当前连接是否已经持有CommandData对象会无限新建,新版本修复了这个问题
这里面需要注意的是Queue的take阻塞慎用,大家尽量用poll(timeout)的方式,一方面规避上述问题,一方面做优雅停服的话 take是做不到的

官网修正这个问题时修复了该问题:https://github.com/redisson/redisson/issues/2647
另外还发现另外一个内存泄漏问题:https://github.com/redisson/redisson/issues/2362
如果用了rblockqueue.take 这个问题在3.12.5以前版本必现

redisson队列原理分析

redisson的延迟原理 
1. 先写入一个延迟队列(zadd)redisson是通过eval脚本写上去的
2. 延迟队列到期后 会在直接导入到目标的 blockqueue上去 目标在redis里面就是个list的数据结构
3.我们消费端通过redissonClient.getBlockingQueue(queueName) 消费