Jedis 连接池 详解
一. JedisPool的几个重要参数介绍1. maxTotal控制连接池里面最多构建多少个Jedis实例。请看下面代码public static void main(String[] args) {JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(2);JedisPool pool = new JedisPool(
一. JedisPool的几个重要参数介绍
1. maxTotal
控制连接池里面最多构建多少个Jedis实例。请看下面代码
public static void main(String[] args) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(2);
JedisPool pool = new JedisPool(config,"172.29.2.10",7000);
while(true) {
Jedis jedis = pool.getResource();
System.err.println(jedis);
}
}
打印结果
redis.clients.jedis.Jedis@2e5d6d97
redis.clients.jedis.Jedis@238e0d81
说明我们最多只能拿到2个实例,在调用getResource就会阻塞当前线程。我们看下main线程堆栈
"main" #1 prio=5 os_prio=0 tid=0x0000000003702800 nid=0xd588 waiting on condition [0x00000000034fe000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x000000076c3dc110> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at org.apache.commons.pool2.impl.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:590)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:432)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:349)
at redis.clients.jedis.util.Pool.getResource(Pool.java:50)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:330)
at Test.main(Test.java:11)
阻塞原因是jedis底层调用了LockSupport的park方法。但是又不可能无限等待下去吧,maxWaitMillis就是最大的等待时间。超时后会报下面异常:
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:439)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:349)
at redis.clients.jedis.util.Pool.getResource(Pool.java:52)
... 2 more
2. blockWhenExhausted
blockWhenExhausted为true时,上面拿不到jedis实例后,线程阻塞。 blockWhenExhausted为false时,上面拿不到jedis实例后,线程不阻塞,而是抛出异常。
public static void main(String[] args) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(2);
config.setBlockWhenExhausted(false);
JedisPool pool = new JedisPool(config,"172.29.2.10",7000);
while(true) {
Jedis jedis = pool.getResource();
System.err.println(jedis);
}
}
---------------------打印结果---------------------
redis.clients.jedis.Jedis@2e5d6d97
redis.clients.jedis.Jedis@238e0d81
Exception in thread "main" redis.clients.jedis.exceptions.JedisExhaustedPoolException: Could not get a resource since the pool is exhausted
at redis.clients.jedis.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:330)
at Test.main(Test.java:13)
Caused by: java.util.NoSuchElementException: Pool exhausted
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:444)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:349)
at redis.clients.jedis.util.Pool.getResource(Pool.java:50)
... 2 more
插曲功能:先教大家一个技能,如何实时监控redis的tcp连接情况
我们先实时监控下redis-server的tcp连接情况
## redis-server占用端口是7000
[root@localhost 7000]# watch -n 1 -d 'netstat -aln | grep 7000 | wc -l'
为什么会有一个,我们看下是哪个
[root@localhost 7000]# netstat -aln | grep 7000
tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN
原来是listen的tcp,不是客户端的,那就不用管了。
3. maxIdle
直接看这么一段程序
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InterruptedException {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(5);
config.setMaxIdle(2);
config.setBlockWhenExhausted(false);
JedisPool pool = new JedisPool(config,"172.29.2.10",7000);
Jedis j1 = pool.getResource();
Jedis j2 = pool.getResource();
Jedis j3 = pool.getResource();
Jedis j4 = pool.getResource();
Jedis j5 = pool.getResource();
Thread.sleep(3000);
j1.close();
j2.close();
j3.close();
Thread.sleep(1000000);
}
先getResource 5次,我们监控插曲功能tcp连接数
马上从1到6,原来本身有一个listen的tcp,说明getResource 5次 后,实在的产生了5个tcp连接。然后执行3次close,理论上应该释放掉3个tcp,然而结果为
(注意:这里要立马看监控才是5,如果等一定的时间的话就会变成3,这也是其余参数控制的,后面讲)
由于我们maxIdle 设置的是2 ,所以有2个归还到连接池了,真正释放tcp的只有1个。
总结
maxIdle实际上才是业务需要的最大连接数,maxTotal 是为了给出余量,所以 maxIdle 不要设置得过小,否则会有new jedis(新连接)开销。
4. timeBetweenEvictionRunsMillis,minEvictableIdleTimeMillis
上面测试,先归还2个到连接池 , 然后等待最多 大约 TimeBetweenEvictionRunsMillis + MinEvictableIdleTimeMillis 时间,连接无操作的话就真正释放那2个tcp连接。
为什么大约是两个时间之和?其实是分为下面两个阶段(检测线程周期执行检测&超时判断)
- timeBetweenEvictionRunsMillis: 空闲资源的检测周期(单位为毫秒)。
- minEvictableIdleTimeMillis : 资源池中资源的最小空闲时间(单位为毫秒),达到此值后空闲资源将被移除。
5. minIdle
等上面TimeBetweenEvictionRunsMillis + MinEvictableIdleTimeMillis 的时间到后,会自动释放tcp连接,但是如果指定了minIdle,就会最少保持minIdle 连接。怎么释放也不会低于minIdle 连接数。
二. Redis服务端的超时配置
redis.conf配置文件也有个timeout配置,当设置为0时,表示永远不超时,当设置为其它数时,表示到了timeout后,会主动断开没有活跃请求的tcp连接。
三. 连接池配置建议
- 建议 maxTotal = maxIdle
这样就避免了连接池伸缩带来的性能干扰。如果您的业务存在突峰访问,建议设置这两个参数的值相等;如果并发量不大或者maxIdle设置过高,则会导致不必要的连接资源浪费。
- 怎么设置maxTotal的值
假如一个redis命令请求到执行需要花1ms,那么一个tcp的QPS就是1*1000。 如果你配置了maxTotal =50 , 也就是同时最大50个tcp连接,那么你服务的总QPS就是1000*50 = 50000。实际上线上服务都是集群的,所以还要乘以你的机器数。
但是 redis 也有最大的连接限制,所以不建议maxTotal很大
########### redis-server 的 最大连接数查看
127.0.0.1:7000> CONFIG GET maxclients
1) "maxclients"
2) "10000"
四. redis服务端定位连接数
查看当前所有连接数
127.0.0.1:7000> CLIENT LIST
id=392 addr=172.29.2.194:50751 fd=14 name= age=32 idle=32 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
id=387 addr=127.0.0.1:51988 fd=8 name= age=125 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=32768 obl=0 oll=0 omem=0 events=r cmd=client
id=388 addr=172.29.2.194:50747 fd=9 name= age=32 idle=32 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
id=389 addr=172.29.2.194:50748 fd=10 name= age=32 idle=32 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
id=390 addr=172.29.2.194:50749 fd=12 name= age=32 idle=32 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
id=391 addr=172.29.2.194:50750 fd=13 name= age=32 idle=32 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=NULL
几个重要字段解释
age
: 以秒计算的已连接时长。idle
: 以秒计算的空闲时长。cmd
: 最近一次执行的命令。
如果线上发现大部分连接 age 的时间 和 idle 时间差不多大小的,证明都是废物空闲连接。
还有其它几个命令
- CLIENT KILL ip:port 杀死指定连接
主动杀死连接
127.0.0.1:7000> CLIENT KILL id 392
强烈推荐一个 进阶 JAVA架构师 的博客
更多推荐
所有评论(0)