JedisPool连接池相关配置

点击上方"IT牧场",选择"设为星标"技术干货每日送达!

本文来源: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

干货分享

最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!

•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术讨论群

近期热文

瓜子二手车在 Dubbo 版本升级、多机房方案方面的思考和实践Java12可用新特性一览聊聊 Java 的几把 JVM 级锁如何设计一个安全的对外接口?SQL 性能优化梳理垃圾回收-实战篇


想知道更多?长按/扫码关注我吧↓↓↓>>>技术讨论群<<<喜欢就点个"在看"呗^_^

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值