JedisPool无法获得资源问题

摘要:
也就是说,已经使用了其中的40个,并且在超时100毫秒内没有资源返回到JedisPool。让我们想象一个例子,从jediPool获取资源的超时时间是100ms,而在程序中获取或设置资源的超时是200ms。这可能会发生。

线上碰到一个问题:
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

at redis.clients.util.Pool.getResource(Pool.java:22)

线上会相隔不定时的天数后出现一次JedisPool种getresouce拿不到resource的情况。中间陆陆续续上过很多次线,然后废了很大劲努力排除掉了业务可能和多次上线的代码问题。业务数据量即便是在测试环境种建造了更多,也不会导致那种情况的出现。而业务代码测试环境和线上相同,后来在测试环境压测的压力和线上差不多的情况下,也不会重现这个问题。
后来就有一种束手无策的感觉了,最后只能推论是当时应用集群到Redis集群的网络出了问题了,但是由于种种原因一直没有在集群间添加网络状态的监控,也就只能是猜测了,但是又没办法重现。后来偷偷在线上的一台服务器上面添加了ping的监控,很简单:ping -i 1 192.168.134.155 > pinglog_{`date +%Y-%m-%d`}.log &,该命令的效果比较简单,就是每隔1sping一次目标服务器,然后打印到按天分开的日志里面。然而这种事情不再出现我们酒没办法验证推论,领导又催的非常紧,没办法还是需要验证出来啊。
开始的时候,根据代码来找原因,代码里面从jedispool种获得jedis资源实例的代码是使用了java7里面的try-with-resouce的写法,也就是用完之后,于是就怀疑是不是这种写法,在try块里面有了其他异常会导致resouce无法正常关闭,导致某个Jedis实例用完后没有还给JedisPool,导致资源不足?

public class JedisTest {
    private static final JedisPool jedisPool;

    static {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxIdle(20);
        config.setMaxTotal(40);
        config.setMinIdle(10);

        jedisPool = new JedisPool(config, "127.0.0.1", 8279, 1000);
    }

    public static void main(String[] args) {
        try(Jedis jedis = jedisPool.getResource()){
            throw new Exception("~");
        }catch (Exception e){
            //do nothing
        }
    }
}

后来其实在JedisPool里面的断点很容易就可以看到java7 并没有错误,多心了。

于是,那还是回归主题,其实只要认真分析,不会又那么困难的问题出现:
其实getresouce报错有两种可能:
1、本身有错误---排除,首先如果这个方法有错误,那么之前应该会一直出现,或者其他人也早该把开源包的错误爆出,排除这种可能;
2、就是在规定时间内没取到资源。
刚才我们看maxtotal里面定义了池子最大就40个,如果真的40个都在用,并且在超时的100ms内没人return resouce,那报错也正常。
也就是说,我们出现了40个全部被用到,并且在超时的100ms内没有任何资源还给JedisPool。
后来恰好,在打印的jstack的信息种发现了大量的time_waiting状态的线程在等待从Jedispool.getResouce().
那么什么情况下会导致这个情况出现?
假设现在并发来了41个请求,然后其中40个正常的进行,但是第41没拿到资源,于是等待规定的超时时间,但是这会从应用到Redis集群间网络出现抖动,暂时不通,会导致40个请求种的里面的jedis的get或者set操作变慢甚至超时。
我们设想一种情况:从jedisPool里面拿资源的超时时间是100ms,程序里面进行get或者set资源的是200ms超时,那么就有可能出现这种情况。
事实证明我们的配置确实是jedis里面去get或者set一个key的时候,超时时间是200ms,那也就是说,如果网络发生了抖动,那就会在并发的情况下迅速耗光资源池,然后超时后报错才还回去,但是那个时间早就发生了getResouce的错误。
Bingo,其实很简单的原因,那就是没有正确的理解两个超时时间之间的关系。
我们可以简单测试一下:

public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(20);
        for(int i = 0;i < 20 ;i ++){
            service.execute(new Runnable() {
                @Override
                public void run() {
                    try(Jedis jedis = jedisPool.getResource()) {
                        Thread.sleep(200L);
                    } catch (InterruptedException e) {
                        System.out.println(e);
                    }
                }
            });
        }
    }

由于本地环境问题,只是示例代码,就不执行了。其实很容易就还原了问题出来。

后面只要调小jedis的get和set方法的超时时间,同时也尽量小的使用getresource的超时时间(这里为什么不加大,因为在高并发的情况下会迅速耗光线程数量,jstack里面甚至出现了500个线程有450个是time_waiting的状态,这可不是我们想要的结果)。

然后在服务器之间添加监控和警报,及时报警进行网络的修复。

免责声明:文章转载自《JedisPool无法获得资源问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇outlook exchange server 强制更新通讯录数据集和JSON相互转换下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Asp.net Mvc 使用EF6 code first 方式连接MySQL总结

最近由于服务器变更为Linux系统.MsSql for Linux什么时候出来到生产环境使用还是要很长时间的.于是考虑使用Mysql数据库,ORM使用EF.于是先踩下坑顺便记录一下,有需要的tx可以参考下.当你考虑使用EF连接Mysql的时候肯定是已经在网上搜了一堆教程.网上教程基本都是使用控制台做演示.跟着一步步来姿势没错的话可能会正常运行,但项目中使用...

Nacos配置的多环境管理

实现多环境管理有下面几种方式 1.使用Data ID与profiles实现 在 Nacos Config Starter 中,dataId 的拼接格式为 ${prefix} - ${spring.profiles.active} . ${file-extension}   prefix 默认为 spring.application.name 的值,可以通过...

webpack+javascript搭建项目环境大概流程和各个流程的功能作用

前言 本文是对项目开发中webpack+原生js搭建流程的步骤梳理,和常见配置项的功能讲解.并不适用于vue/react框架 因为实际项目上的配置代码过多,放在一章写太长了,怕看不下去,所以实际代码另写在这里,两篇文章可以搭配看:https://www.cnblogs.com/liuXiaoDi/p/12245421.html webpcak里配置的路径都...

axios用headers传参,设置请求头token

新建一个配置文件http.js   // 导入axios import axios from 'axios'; // 全局配置默认路由 axios.defaults.baseURL = 'http://192.168.0.157:8989/xxxx/xxx/'; axios.interceptors.request.use(function (confi...

MySQL&amp;amp;ES连接池

 数据库的连接池建议放在类似settings.py的配置模块中,因为基本都是配置项,方便统一管理。 1) 连接池类#settings.py import os from DBUtils.PooledDB import PooledDB from elasticsearch import Elasticsearch import pymysql class...

log.io日志实时显示

Node.js 安装 菜鸟教程Node.js安装 设置 npm 淘宝镜像 npm config set registry https://registry.npm.taobao.org npm config get registry log.io 简介 官网地址github地址 安装服务端 npm install -g log.io 配置服务端 找...