-----------------------------------------
private volatile static JedisPool pool = null; //本地测试
private volatile static JedisSentinelPool sentinelPool = null;
private static GenericObjectPoolConfig config = null;
private static final int TIMEOUT = 10000; // 10秒
static {
config = new JedisPoolConfig();
config.setMaxTotal(500);
// 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
config.setMaxIdle(5);
// 表示当borrow(引入)一个jedis实例时,最大的等待时间,若是超过等待时间,则直接抛出JedisConnectionException;
config.setMaxWaitMillis(1000 * 60 * 1000);
// 在borrow一个jedis实例时,是否提早进行validate操做;若是为true,则获得的jedis实例均是可用的;
config.setTestOnBorrow(false);
}
public static void init(String host, int port, String password) {
if(pool == null) {
synchronized(RedisUtils.class) {
if(pool == null) {
pool = new JedisPool(config, checkNotNull(host), checkNotNull(port), TIMEOUT,checkNotNull(password));
}
}
}
}
-----------------------------------------
-----------------------------------------
//初始化调用
RedisUtils.init(host, port, password);
-----------------------------------------
-----------------------------------------
//得到 redis 资源 与 释放 redis 资源
/**
* 获取jedis资源
*
* @return
*/
public static Jedis getJedis() {
Jedis jedis = null;
try {
if(pool != null) {
jedis = pool.getResource();
} else {
jedis = sentinelPool.getResource();
}
} catch (Throwable t) {
logger.error("", t);
}
return jedis;
}
/**
* 归还jedis资源
*
* @param jedis
*/
@SuppressWarnings("deprecation")
public static void returnResource(Jedis jedis) {
try {
if(pool != null) {
pool.returnResource(jedis);
} else {
sentinelPool.returnResource(jedis);
}
} catch (Throwable t) {
logger.error("", t);
if(pool != null) {
pool.returnBrokenResource(jedis);
} else {
sentinelPool.returnBrokenResource(jedis);
}
}
}
-----------------------------------------
-----------------------------------------
数据操做,可使用 Pipeline 操做:
Pipeline pipeline = jedis.pipelined();
Response<Long> response = pipeline.sadd(key, member);
pipeline.expireAt(key, expireTime);
-----------------------------------------
JedisPool保证资源在一个可控范围内,而且提供了线程安全,可是一个合理的GenericObjectPoolConfig配置能为应用使用Redis保驾护航,下面将对它的一些重要参数进行说明和建议:java
在当前环境下,Jedis链接就是资源,JedisPool管理的就是Jedis链接。node
序号 | 参数名 | 含义 | 默认值 | 使用建议 |
---|---|---|---|---|
1 | maxTotal | 资源池中最大链接数 | 8 | 设置建议见下节 |
2 | maxIdle | 资源池容许最大空闲的链接数 | 8 | 设置建议见下节 |
3 | minIdle | 资源池确保最少空闲的链接数 | 0 | 设置建议见下节 |
4 | blockWhenExhausted | 当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效 | true | 建议使用默认值 |
5 | maxWaitMillis | 当资源池链接用尽后,调用者的最大等待时间(单位为毫秒) | -1:表示永不超时 | 不建议使用默认值 |
6 | testOnBorrow | 向资源池借用链接时是否作链接有效性检测(ping),无效链接会被移除 | false | 业务量很大时候建议设置为false(多一次ping的开销)。 |
7 | testOnReturn | 向资源池归还链接时是否作链接有效性检测(ping),无效链接会被移除 | false | 业务量很大时候建议设置为false(多一次ping的开销)。 |
8 | jmxEnabled | 是否开启jmx监控,可用于监控 | true | 建议开启,但应用自己也要开启 |
空闲Jedis对象检测,下面四个参数组合来完成,testWhileIdle是该功能的开关。redis
序号 | 参数名 | 含义 | 默认值 | 使用建议 |
---|---|---|---|---|
1 | testWhileIdle | 是否开启空闲资源监测 | false | true |
2 | timeBetweenEvictionRunsMillis | 空闲资源的检测周期(单位为毫秒) | -1:不检测 | 建议设置,周期自行选择,也能够默认也可使用下面JedisPoolConfig中的配置 |
3 | minEvictableIdleTimeMillis | 资源池中资源最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除 | 1000 60 30 = 30分钟 | 可根据自身业务决定,大部分默认值便可,也能够考虑使用下面JeidsPoolConfig中的配置 |
4 | numTestsPerEvictionRun | 作空闲资源检测时,每次的采样数 | 3 | 可根据自身应用链接数进行微调,若是设置为-1,就是对全部链接作空闲监测 |
为了方便使用,Jedis提供了JedisPoolConfig,它自己继承了GenericObjectPoolConfig设置了一些空闲监测设置apache
public class JedisPoolConfig extends GenericObjectPoolConfig { public JedisPoolConfig() { // defaults to make your life with connection pool easier :) setTestWhileIdle(true); // setMinEvictableIdleTimeMillis(60000); // setTimeBetweenEvictionRunsMillis(30000); setNumTestsPerEvictionRun(-1); } }
全部默认值能够从org.apache.commons.pool2.impl.BaseObjectPoolConfig中看到。数组
实际上这个是一个很难回答的问题,考虑的因素比较多:安全
以一个例子说明,假设:服务器
那么理论上须要的资源池大小是50000 / 1000 = 50个。但事实上这是个理论值,还要考虑到要比理论值预留一些资源,一般来说maxTotal能够比理论值大一些。网络
但这个值不是越大越好,一方面链接太多占用客户端和服务端资源,另外一方面对于Redis这种高QPS的服务器,一个大命令的阻塞即便设置再大资源池仍然会无济于事。并发
maxIdle实际上才是业务须要的最大链接数,maxTotal是为了给出余量,因此maxIdle不要设置太小,不然会有new Jedis(新链接)开销,而minIdle是为了控制空闲资源监测。性能
链接池的最佳性能是maxTotal = maxIdle ,这样就避免链接池伸缩带来的性能干扰。可是若是并发量不大或者maxTotal设置太高,会致使没必要要的链接资源浪费。
能够根据实际总OPS和调用redis客户端的规模总体评估每一个节点所使用的链接池。
实际上最靠谱的值是经过监控来获得“最佳值”的,能够考虑经过一些手段(例如jmx)实现监控,找到合理值。
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Timeout waiting for idle object at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:449)
或者
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool … Caused by: java.util.NoSuchElementException: Pool exhausted at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:464)
两种状况均属于没法从资源池获取到资源,但第一种是超时,第二种是由于blockWhenExhausted为false根本就不等。
遇到此类异常,不要盲目的认为资源池不够大,第三节已经进行了分析。具体缘由能够排查:网络、资源池参数设置、资源池监控(若是对jmx监控)、代码(例如没执行jedis.close())、慢查询、DNS等问题。
具体能够参考该文章:https://www.atatech.org/articles/77799
因为一些缘由(例如超时时间设置较小缘由),有的项目在启动成功后会出现超时。JedisPool定义最大资源数、最小空闲资源数时,不会真的把Jedis链接放到池子里,第一次使用时,池子没有资源使用,会new Jedis,使用后放到池子里,可能会有必定的时间开销,因此也能够考虑在JedisPool定义后,为JedisPool提早进行预热,例如以最小空闲数量为预热数量
List<Jedis> minIdleJedisList = new ArrayList<Jedis>(jedisPoolConfig.getMinIdle()); for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) { Jedis jedis = null; try { jedis = pool.getResource(); minIdleJedisList.add(jedis); jedis.ping(); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { } } for (int i = 0; i < jedisPoolConfig.getMinIdle(); i++) { Jedis jedis = null; try { jedis = minIdleJedisList.get(i); jedis.close(); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { } }