/**
* 定时更新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连接池也可以类似思路更新。