JedisPool
使用的最佳实践(阿里云上现成的,其实就是对Apache-commons-pool2这个库的理解,网上找的七七八八的都是说的GenericObjectPool的配置导致的一些timeout、QPS、no resource之类的问题)
参数说明
Jedis连接就是连接池中JedisPool管理的资源,JedisPool保证资源在一个可控范围内,并且保障线程安全。使用合理的GenericObjectPoolConfig配置能够提升Redis的服务性能,降低资源开销。下列两表将对一些重要参数进行说明,并提供设置建议。
表 1. 资源设置与使用相关参数
| 参数 | 说明 | 默认值 | 建议 |
|---|---|---|---|
| maxTotal | 资源池中的最大连接数 | 8 | 参见下面关键参数设置建议。 |
| maxIdle | 资源池允许的最大空闲连接数 | 8 | 参见下面关键参数设置建议。 |
| minIdle | 资源池确保的最少空闲连接数 | 0 | 参见下面关键参数设置建议。 |
| blockWhenExhausted | 当资源池用尽后,调用者是否要等待。只有当值为true时,下面的maxWaitMillis才会生效。 | true | 建议使用默认值。 |
| maxWaitMillis | 当资源池连接用尽后,调用者的最大等待时间(单位为毫秒)。 | -1(表示永不超时) | 不建议使用默认值。 |
| testOnBorrow | 向资源池借用连接时是否做连接有效性检测(ping)。检测到的无效连接将会被移除。 | false | 业务量很大时候建议设置为false,减少一次ping的开销。 |
| testOnReturn | 向资源池归还连接时是否做连接有效性检测(ping)。检测到无效连接将会被移除。 | false | 业务量很大时候建议设置为false,减少一次ping的开销。 |
| jmxEnabled | 是否开启JMX监控 | true | 建议开启,请注意应用本身也需要开启。 |
空闲Jedis对象检测由下列四个参数组合完成,testWhileIdle是该功能的开关。
表 2. 空闲资源检测相关参数
| 名称 | 说明 | 默认值 | 建议 |
|---|---|---|---|
| testWhileIdle | 是否开启空闲资源检测。 | false | true |
| timeBetweenEvictionRunsMillis | 空闲资源的检测周期(单位为毫秒) | -1(不检测) | 建议设置,周期自行选择,也可以默认也可以使用下方JedisPoolConfig 中的配置。 |
| minEvictableIdleTimeMillis | 资源池中资源的最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除。 | 1800000(即30分钟) | 可根据自身业务决定,一般默认值即可,也可以考虑使用下方JeidsPoolConfig中的配置。 |
| numTestsPerEvictionRun | 做空闲资源检测时,每次检测资源的个数。 | 3 | 可根据自身应用连接数进行微调,如果设置为 -1,就是对所有连接做空闲监测。 |
为了方便使用,Jedis提供了JedisPoolConfig,它继承了GenericObjectPoolConfig在空闲检测上的一些设置。
关键参数设置建议
maxTotal(最大连接数)
想合理设置maxTotal(最大连接数)需要考虑的因素较多,如:
- 业务希望的Redis并发量;
- 客户端执行命令时间;
- Redis资源,例如nodes (如应用个数等) * maxTotal不能超过Redis的最大连接数;
- 资源开销,例如虽然希望控制空闲连接,但又不希望因为连接池中频繁地释放和创建连接造成不必要的开销。
假设一次命令时间,即borrow|return resource加上Jedis执行命令 ( 含网络耗时)的平均耗时约为20us,一个连接的QPS大约是50000,而业务期望的QPS是500000,那么理论上需要的资源池大小是500000 / 50000 = 10。
但事实上这只是个理论值,除此之外还要预留一些资源,所以maxTotal可以比理论值大一些。这个值不是越大越好,一方面连接太多会占用客户端和服务端资源,另一方面对于Redis这种高QPS的服务器,如果出现大命令的阻塞,即使设置再大的资源池也无济于事。
maxIdle与minIdle
maxIdle实际上才是业务需要的最大连接数,maxTotal 是为了给出余量,所以 maxIdle 不要设置得过小,否则会有new Jedis(新连接)开销,而minIdle是为了控制空闲资源检测。
连接池的最佳性能是maxTotal=maxIdle,这样就避免了连接池伸缩带来的性能干扰。但如果并发量不大或者maxTotal设置过高,则会导致不必要的连接资源浪费。
您可以根据实际总QPS和调用Redis的客户端规模整体评估每个节点所使用的连接池大小。
使用监控获取合理值
在实际环境中,比较可靠的方法是通过监控来尝试获取参数的最佳值。可以考虑通过JMX等方式实现监控,从而找到合理值。
常见问题
资源不足
下面两种情况均属于无法从资源池获取到资源。
-
超时:
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool ...``Caused by: java.util.NoSuchElementException: Timeout waiting ``foridle object``at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:``449``) -
blockWhenExhausted 为false,因此不会等待资源释放:
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``)
此类异常的原因不一定是资源池不够大,请参见关键参数设置建议中的分析。建议从网络、资源池参数设置、资源池监控(如果对JMX监控)、代码(例如没执行jedis.close())、慢查询、DNS等方面进行排查。
预热JedisPool
由于一些原因(如超时时间设置较小等),项目在启动成功后可能会出现超时。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 { ``}``} |
|---|
lettuce
TODO
路图
最佳实践:
密码:6666
监控指标:
密码:6666