redis客户端testOnBorrow配置的作用

前几天出现了一个redis线上问题,其中涉及到了redis客户端的testOnBorrow配置,今天通过我们的线上问题,来说说redis客户端testOnBorrow配置的作用

redis线上问题

redis流量过大造成报警

我们一个服务连接的redis服务突然报警了,报警内容为:

  • 访问redis服务的qps超过十万

看监控发现redis的qps超过十万

查询平台监控发现qps超过了十万


redis客户端testOnBorrow配置的作用_第1张图片
qps达到了12万

造成redis查询性能降低

  • redis的qps达到了12万,我们的redis是副本形式的,极限的qps是10万以下,超过这个值,就会影响到性能
  • 当时我们的redis服务受到影响查询时间已经到500ms


    redis客户端testOnBorrow配置的作用_第2张图片
    redis响应时间达到了500ms

分析问题

1.先查看流量分布

redis客户端testOnBorrow配置的作用_第3张图片
image.png
  • 查看了redis的命令流量分布,主要用到的流量是hmget命令,qps达到3万6,是真实的服务流量,但是有7万2的qps,感觉不是很正常,看代码逻辑

2.看代码逻辑

  • 1.我们的服务中redis是用作缓存,主要使用hash结构来缓存数据,提高接口查询速度
  • 2.当redis查询不到、或者redis查询超时时候会从底层服务查询,来保证服务可靠性
  • 3.该服务代码中使用了我们同学自己封装的redis客户端
  • 4.redis配置中testOnBorrow属性设置为了true
  • 5.testOnBorrow配置的作用为,在从池中取出连接前是否进行检验,如果检验失败,则从池中去除连接并尝试取出另一个。true为检查,false为不检查。设置为true时,当连接池中的连接全部被占后,导致后续操作,获取不到数据,这些程序会等待,直到超时或者获取到有效连接为主
  • 6.连接校验的方式就是向服务发送ping命令,源码如下:
boolean validate = false;
            Throwable validationThrowable = null;

            try {
                validate = this.factory.validateObject(p);
            } catch (Throwable var15) {
                PoolUtils.checkRethrow(var15);
                validationThrowable = var15;
            }

            if (!validate) {
                try {
                    this.destroy(p);
                    this.destroyedByBorrowValidationCount.incrementAndGet();
                } catch (Exception var14) {
                }

                p = null;
                if (create) {
                    NoSuchElementException nsee = new NoSuchElementException("Unable to validate object");
                    nsee.initCause(validationThrowable);
                    throw nsee;
                }
            }
        }

校验的逻辑为发送ping命令

            return hostAndPort.getHost().equals(connectionHost) && hostAndPort.getPort() == connectionPort && jedis.isConnected() && jedis.ping().equals("PONG");

  • 7.这样的配置,理论上ping和正常流量是1比1,但是现在ping是2比1
  • 8.查看我们同学封装redis客户端源码发现,这个客户端主要是为了集群模式redis准备的,他封装了一个JedisClient对象,每个JedisClient对应链接到一个redis集群节点上,获取redis连接过程为,先获取jedisClient对象,在用JedisClient对象获取redis链接,JedisClient对象获取连接时,也会使用jedis发送一次ping命令
String ping = jedis == null ? "" : jedis.ping();
  • 9.所以当我们testOnBorrow属性设置为了true时,获取一次jedis连接时,会发送2次ping命令,造成了ping命令的流量为正常流量的两倍
  • 10.实验验证:把我们同学封装的redis客户端替换成了jedis包后,把testOnBorrow属性设置为了true,测试接口为 ping流量和正常流量比例1比1

解决方式

1.把我们同学自己封装的redis包换成了jedis3.2版本,解决了每次获取连接ping的问题

2.由于我们redis只是用作缓存,查询不到时,会调用底层服务查询,所以每次查询不是强依赖与redis,所以我们把testOnBorrow属性设置为true,当连接池中获取不到连接时,查询走底层服务,保证服务的可靠性

3.目前现状

  • 流量高峰时,redis的qps稳定在5万以下


    redis客户端testOnBorrow配置的作用_第4张图片
    image.png
  • 服务响应时间也稳定在10ms左右

你可能感兴趣的