问题描述:
记一次jedis导致服务器假死的解决方案
2020.9.20 20:00左右
加油贝线上商城出现连接失败,所有客户端出现页面转圈问题,上午就出现了两次,当时没有解决,直接重启,傍晚又来一次,不行了,群里炸锅了.
原因分析:
先介绍一下服务器配置,4核16g带宽3m的服务器(后面用的到)
先打开xshell,连接到服务器,查看服务进程是否存在,一看进程,还在,那就有的解决,开心的一批😄,开始着手排查,先top 查看cpu占用,发现cpu利用率百分一都不到,内存也是正常,此时心里有点波动,那就继续看看是不是死循环或者是死锁的问题,排查线程,top -Hp -pid 查看进程下所有线程状态,发现所有线程尽然全部阻塞,只好放大招了 jstack -pid 生成线程快照
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006addee0c8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:524)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:433)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:360)
at redis.clients.util.Pool.getResource(Pool.java:40)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:84)
at com.sohu.snscommon.cluster.service.impl.OrMetaRedisClusterServiceImpl.getJd(OrMetaRedisClusterServiceImpl.java:111)
at com.sohu.snscommon.cluster.service.impl.OrMetaRedisClusterServiceImpl.getItem(OrMetaRedisClusterServiceImpl.java:241)
at com.sohu.sns_protobuf.service.impl.SnsProtoCacheServerImpl.loadCache(SnsProtoCacheServerImpl.java:44)
at com.sohu.sns_protobuf.service.impl.SnsPersServerImpl.loadItemJson(SnsPersServerImpl.java:167)
at com.sohu.sns_protobuf.utils.PersistenceUtil.getItem(PersistenceUtil.java:109)
at com.sohu.sns.service.pull.service.FeedPullService.getUserRelation(FeedPullService.java:215)
at com.sohu.sns.service.pull.service.FeedPullService.addPullMarkInternal(FeedPullService.java:172)
at com.sohu.sns.service.pull.service.FeedPullService.addPullMark(FeedPullService.java:153)
at com.sohu.sns.service.pull.mq.PullServerMQConsumer$1.run(PullServerMQConsumer.java:92)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.run(FutureTask.java:262)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
发现是jedisPool.getRecouce阻塞了,原来是jedis的问题,既然知道是什么,那就开始解决,
解决方案:
项目依赖 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
发现是jedisPool.getRecouce阻塞了,原来是jedis的问题,既然知道是什么,那就开始解决, 看一下配置文件,发现有问题了 max-wait: -1ms 获取不到recource尽然要一直阻塞
jedis:
pool:
max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 8 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
现在开始调配jedis
jedis:
pool:
max-active: 4 # 连接池最大连接数(使用负值表示没有限制)
max-wait: 50ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 4 # 连接池中的最大空闲连接
min-idle: 2 # 连接池中的最小空闲连接
这是调整后的配置 简单说一下这个配置的依据,之前服务器的配置是4核16g3m的,线程最大数是 核数4 由于redis涉及到io操作,核数就是线程数,当线程不涉及到io操作时,线程数可以设置为 核数*2+1 ,最小空闲呢就根据需求设置,这里我选择用2个线程 再次上线,问题解决
注意: 用jedis就要对jedis配置熟悉,避免不需要的麻烦 此时还有一种解决方案,这种解决方案是可以用lettuce 来带替jedis,springboot2.1.3默认的spring-boot-starter-data-redis默认实现就是lettuce
到此为止,从排查到结束没用20分钟就解决了问题,对大家要是有所帮助,帮忙点个赞,关注一下公众号,谢谢啦😊