动态更新自建的Redis连接池连接数量

发布于:2024-06-20 ⋅ 阅读:(65) ⋅ 点赞:(0)
/**
     * 定时更新Redis连接池信息,防止资源让费
     */
    private static final ScheduledThreadPoolExecutor DYNAMICALLY_UPDATE_REDIS_POOL_THREAD = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            /**
             * 通道检查,对未成功注册路由的设备补偿注册
             */
            thread.setName("dynamically.update.redis.pool");
            return thread;
        }
    });
static {
        DYNAMICALLY_UPDATE_REDIS_POOL_THREAD.scheduleAtFixedRate(() -> {
            try {
                dynamicallyUpdateRedisPool();
            } catch (Exception e) {
                logger.warn("Redis扩容缩容失败", e);
            }
            //开机延迟5分钟,之后每1分钟执行一次
        }, 5, 1, TimeUnit.MINUTES);
    }
/**
     * 动态更新连接池信息
     */
    private static void dynamicallyUpdateRedisPool() {
        if (instMap.isEmpty()) {
            return;
        }
        String key = null;
        MyJedis myJedis = null;
        int maxConn = 0, activeNum = 0, idleNum = 0, waiterNum = 0, newMaxConn = 0;
        long maxWaitTime = 0, meanWaitTime = 0;
        boolean isUpdateConn = false;
        for (Entry<String, MyJedis> keyMyJedisEntry : instMap.entrySet()) {
            isUpdateConn = false;
            key = keyMyJedisEntry.getKey();
            myJedis = keyMyJedisEntry.getValue();
            if (myJedis == null || myJedis.pool == null || myJedis.pool.isClosed()) {
                continue;
            }
            maxConn = myJedis.maxConnection;
            //活跃连接诶数量
            activeNum = myJedis.pool.getNumActive();
            //monitor(key + ".active.num", activeNum, null);
            //空闲连接数量
            idleNum = myJedis.pool.getNumIdle();
            //monitor(key + ".idle.num", idleNum, null);
            //等待连接数量
            waiterNum = myJedis.pool.getNumWaiters();
            //monitor(key + ".waiter.num", waiterNum, null);
            //等待连接最长时间毫秒
            maxWaitTime = myJedis.pool.getMaxBorrowWaitTimeMillis();
            //monitor(key + ".max.wait.time", null, maxWaitTime);
            //等待连接平均毫秒
            meanWaitTime = myJedis.pool.getMeanBorrowWaitTimeMillis();
            //monitor(key + ".mean.wait.time", null, meanWaitTime);
            // 判断连接数是否超出预期范围
            if (activeNum > maxConn * 0.8) {
                logger.warn("警告:活跃连接数过多,可能需要优化连接池设置 activeNum:{} maxConn:{}。", activeNum, maxConn);
                isUpdateConn = true;
            } else if (idleNum < MAX_IDLE * 0.2) {
                logger.warn("警告:空闲连接数过少,可能需要优化连接池设置 idleNum:{} maxIdle:{}。", idleNum, MAX_IDLE);
                isUpdateConn = true;
            }
            if (isUpdateConn) {
                newMaxConn = Double.valueOf(maxConn * (1 + 0.25)).intValue();
                if (newMaxConn >= REDIS_MAX_CONN) {
                    logger.warn("警告:redis已达可申请的最大连接数量,不能继续扩容 maxConn:{} redisScalesUpTheMost:{}", maxConn, REDIS_MAX_CONN);
                    continue;
                }
                updateJedisPool(myJedis, newMaxConn);
                continue;
            }
            // 当空闲连接过多,并且总连接数小于最大值的0.2
            if (idleNum > MIN_IDLE && activeNum < maxConn * 0.2) {
            	logger.warn("警告:空闲连接过多,活跃连接太少 idleNum:{} minIdle:{} activeNum:{} maxConn:{}。", idleNum, MIN_IDLE, activeNum, maxConn);
                newMaxConn = Double.valueOf(maxConn * 0.75).intValue();
                if (newMaxConn <= REDIS_MIN_CONN) {
                    logger.warn("警告:redis已达缩容的最小连接数量,不能继续缩容 maxConn:{} redisScalesUpTheMost:{}", maxConn, REDIS_MIN_CONN);
                    continue;
                }
                updateJedisPool(myJedis, newMaxConn);
            }
        }
    }

    private static void updateJedisPool(MyJedis myJedis, int newMaxConn) {
        //空闲连接数为空 & 当前活跃连接数量,已达最大连接数量 & 最大等待时间达到了 5s & 平均等待时间达到了 1s,连接池扩大0.5倍
        JedisPool oldJedisPool = myJedis.pool;
        myJedis.pool = initJedisPool(myJedis, newMaxConn);
        myJedis.maxConnection = newMaxConn;
        try {
            Thread.sleep(5000);
            //等待5s,防止redis访问还在使用,之后回收老的连接池
            oldJedisPool.destroy();
        } catch (InterruptedException e) {
        }
    }
/**
     * 初始化Redis连接信息
     *
     * @param maxConn
     * @return
     */
    private static JedisPool initJedisPool(MyJedis myJedis, int maxConn) {
        JedisPoolConfig config = new JedisPoolConfig();
        //最大连接数
        config.setMaxTotal(maxConn);
        //最大空闲连接数
        config.setMaxIdle(MAX_IDLE);
        //最小空闲连接数
        config.setMinIdle(MIN_IDLE);
        //获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
        config.setMaxWaitMillis(1000 * 10);
        //在获取连接的时候检查有效性
        config.setTestOnBorrow(false);
        //返回连接时检查有效性
        config.setTestOnReturn(false);
        //空闲时检查有效性
        config.setTestWhileIdle(true);
        if (StringUtils.isNoneBlank(myJedis.password)) {
            return new JedisPool(config, myJedis.host, myJedis.port, 8000, myJedis.password, myJedis.database);
        } else {
            return new JedisPool(config, myJedis.host, myJedis.port, 8000, null, myJedis.database);
        }
    }

Spring bean的Redis连接池也可以类似思路更新。


网站公告

今日签到

点亮在社区的每一天
去签到