这几天线上出现了偶发性jedis的异常,堆栈如下
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. |
看jedis的源码如下:
private void ensureFill() throws JedisConnectionException { |
看起来是连接关闭的问题,但是为什么会出现呢?
我们看了当时出问题的时间点附近的redis连接数,发现了点东西
时间点上比较接近
但是又不完全对的上时间
也就是峰值过后的第十分钟才出现,查看了jedis的配置文件如下:
redis.maxTotal = 5000000 // 值的合理性先不管他 |
一开始,我们是怀疑jedis使用的commons-pool的空闲连接回收策略导致的,按上面的配置文件
每60s执行一次,每次检查3个连接,检查的条件是存活了5min
那我就是写代码模拟一下这种情况,连接暴涨之后,因为回收策略的问题,导致的连接使用的问题,但是我不管怎么配置,都不能模拟出Unexpected end of stream
的情况,那我就怀疑到底是不是因为回收的问题,因为上面的回收配置来看,10分钟也释放不了那么多空闲连接(1次/min 10min 3 = 30)
那十分钟这个到底怎么来的,困扰了我几个小时,后来我去查看了一些redis的配置,好像想起来了什么
我们redis的配置文件设置的是600s
超时,也就是10
分钟
127.0.0.1:6379> config get timeout |
看起来非常像了,那我再模拟一下,我把本地的redis的超时时间设置成2s,然后,我来试一下
fun main(args: Array<String>) { |
激动得我差点留下了悔恨的泪水
redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. |
2s 连接空闲,redis 回了一个fin
包,来关闭这个连接
那我们再拿来用,就抛了JedisConnectionException: Unexpected end of stream
的异常
当然你会说,我们实际使用的例子,用完一次就会换回去池子里,不会拿了连接,发一次命令,过一会再发
其实这个不是个什么问题,本质上是一样的,不信我们来试一下,这次我们把连接池大小设置的小一点,比如1
fun main(args: Array<String>) { |
嗯,出现了跟线上一模一样的堆栈
Exception in thread "main" org.springframework.data.redis.RedisConnectionFailureException: Unexpected end of stream.; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. |
那我们线上的原因可能就是:
峰值出现的时候,我们拿了很多的连接,这些连接在峰值过去以后,10min,被redis超时断掉了,但是我们commons-pool没有去检查这些连接的是否可用,导致拿到了断掉的连接
我们怎么避免上面的情况出现呢?
目前来看,牺牲一部分性能开启testOnBorrow
是比较稳妥的,就是每次拿连接,都去ping一下,看连接是否可用,不可用就重建连接,我可以试一下,开了以后,程序完全正常
上面配置还有哪些问题呢?
- 连接设置的过大,推荐200以下
- 空闲连接检查设置的不合理,现在看起来好像没有太大作用
- 客户端最大timeout设置的过大