dbcp2连接池配置_JedisPool连接池相关配置

点击上方☝ SpringForAll社区  轻松关注! 及时获取有趣有料的技术文章

本文来源:http://yeming.me/2017/07/08/jedisPoolConfig/

最近有些其他业务部门的同学在线上环境redis有出现以下错误Unexpected end of stream,这个错误大致是因为,redis服务器端已经关闭了客户端的连接,而客户端不知道依然拿着原来的连接去访问redis服务器,结果就会报出这个exception。既然我们知道原因是服务器端主动关闭与客户端连接,那么我们下面看下有哪些情况会导致服务器端主动关闭连接。主要在redis.conf中有以下两个参数client-output-buffer-limit、timeout,下面我们分别来介绍下这两个参数的作用。

client-output-buffer-limit

对于Redis服务器的输出(也就是命令的返回值)来说,其大小通常是不可控制的。有可能一个简单的命令,能够产生体积庞大的返回数据。另外也有可能因为执行了太多命令,导致产生返回数据的速率超过了往客户端发送的速率,这是也会导致服务器堆积大量消息,从而导致输出缓冲区越来越大,占用过多内存,甚至导致系统崩溃。redis有以下两种限制来避免出现这种情况,并且不同类型的客户端可以采取不同的配置。

  1. 大小限制:若客户端缓冲区超过某一个设定的值,直接关闭客户端连接。

  2. 持续性限制:若客户端缓冲区连续一定时间内一直超过某个设定的值时,则关闭客户端连接。

下面是一个配置的demo:

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb 60

client-output-buffer-limit pubsub 8mb 2mb 60

redis把客户端分为3种,一种是普通的客户端,比如应用程序获取的jedis连接、普通的redis-cli,上面配置的三个0意思是对于客户端缓冲区无限制;第二种slave客户端,上面配置的3个参数的意思是若缓冲区超过256m则redis服务器直接关闭客户端连接,或者持续60s缓冲区一直超过64m也会关闭客户端连接。第三种是pub/sub客户端配置。

通过以上几种不同的配置,redis就可以避免客户端缓冲区过大导致占用过大内存引起的问题。后来确认了,其他项目组的同学有一个大key超过了50m(我们配置的是30m),直接导致redis服务器关闭了连接。但是应用程序还是从jedis连接池中获取到了这个被关闭的连接去get的时候,就抛出了Unexpected end of stream异常。

timeout配置

redis为了避免客户端连接数过多,有一个timeout配置,意思是如果连接的空闲时间超过了timeout的值,则关闭连接。默认配置是0,意思是没有超时限制,永远不关闭连接。生产上显然不会配置0,我们生产上配置的是120。这个timeout参数配置,要跟客户端创建的连接池的参数配合起来一起使用。下面我们看下jedis连接池的的一个配置demo。

JedisPool jedisPool = new JedisPool(getPoolConfig(), "localhost");

private static GenericObjectPoolConfig getPoolConfig() {

GenericObjectPoolConfig conf = new GenericObjectPoolConfig();

// 设置获取连接的最大等待时间

conf.setMaxWaitMillis(poolWaitMillis);

// 设置最大连接数

conf.setMaxTotal(poolMaxTotal);

// 设置最大空闲连接数

conf.setMaxIdle(poolMaxIdle);

// 设置最小空闲连接数

conf.setMinIdle(poolMinIdle);

// 设置获取连接时不进行连接验证(通过 PoolableObjectFactory.validateObject() 验证连接是否有效)

conf.setTestOnBorrow(false);

// 设置退还连接时不进行连接验证(通过 PoolableObjectFactory.validateObject() 验证连接是否有效)

conf.setTestOnReturn(false);

// 设置连接空闲时进行连接验证

conf.setTestWhileIdle(true);

// 设置连接被回收前的最大空闲时间

conf.setMinEvictableIdleTimeMillis(5 * 60000);

// 设置检测线程的运行时间间隔

conf.setTimeBetweenEvictionRunsMillis(60000);

// 设置检测线程每次检测的对象数

conf.setNumTestsPerEvictionRun(-1);

return conf;

}

上述代码构建了一个JedisPool,我们可以看到jedis使用apache common-pool2来创建一个jedis连接池的。创建jedis连接池传入了一个GenericObjectPoolConfig配置参数,我只筛选了其中一些主要的属性来说明,有兴趣的同学可以自己去看common-pool2的源码。我们主要看下最下面的几个参数(第16行到24行),我们设置了空闲时验证,连接被回收前最大空闲时间为300s,设置检测线程的运行时间间隔是60s,设置线程每次检测的对象数是-1(如果为负数,则检测所有空闲线程)

private int getNumTests() {

int numTestsPerEvictionRun = this.getNumTestsPerEvictionRun();

return numTestsPerEvictionRun >= 0?

Math.min(numTestsPerEvictionRun, this.idleObjects.size()):

(int)Math.ceil((double)this.idleObjects.size() / Math.abs((double)numTestsPerEvictionRun));

}

我们redis服务端配置了timeout是120s,而我们客户端线程检测间隔是60s,每隔60s就会对所有空闲的连接进行检验,会调用JedisFactory的validateObject方法

class JedisFactory implements PooledObjectFactory<Jedis> {

@Override

public boolean validateObject(PooledObject<Jedis> pooledJedis) {

final BinaryJedis jedis = pooledJedis.getObject();

try {

HostAndPort hostAndPort = this.hostAndPort.get();

String connectionHost = jedis.getClient().getHost();

int connectionPort = jedis.getClient().getPort();

return hostAndPort.getHost().equals(connectionHost)

&& hostAndPort.getPort() == connectionPort && jedis.isConnected()

&& jedis.ping().equals("PONG");

} catch (final Exception e) {

return false;

}

}

}

可以看到以上代码最后有一个jedis.ping().equals(“PONG”),这样能保证那些没有被客户端使用的连接每60s之内都能向redis服务端发送心跳,这个时间在我们配置的timeout 120s范围内,redis服务端也就不会关闭这个连接。

参考

  • redis配置详解

http://www.cnblogs.com/kreo/p/4423362.html

  • common-dbcp2数据库连接池参数说明

http://bsr1983.iteye.com/blog/2092467

  • common-pool2连接池详解与使用

http://blog.csdn.net/u011277123/article/details/53943351

  • jedis Issue 932

https://github.com/xetorthio/jedis/issues/932

  • jedis Issue 1029

https://github.com/xetorthio/jedis/issues/1029

d359c73295329c108347d7e6b8568b10.gif

● Hystrix初探

● 别再关注删库跑路了,谈谈数据库架构

● 那些年非常火的MyCAT是什么?

● Java14带来了许多新功能

● 关于烂代码的那些事(上)

● 关于整洁代码的那些事(中)

● 关于整洁代码的那些事(下)

● 面试官说Spring AOP 实现原理给我说说

● 深入理解JVM - 方法调用

● Spring Boot神操作-多个数据源Service层封装

● Lombok经常用,但是你知道它的原理是什么吗?

681b7caa1b6707e0d92d11f5ea062a65.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值