ShardedJedisPool的连接池参数如何设置

1.场景:

客户端采用客户端分片使用redis集群(即每个redis之间无关联,每个redis都是master角色)

1.1说明:

由于采用客户端hash,加上数据不可能均匀,因此最慢的一台redis就是天花板,所以下面都是按照一台redis的量来预估,这样是考虑最坏情况来进行容量评估;

2要面对的问题:

  1. 单台客户端(指连接redis的服务)最多用多少连接?
  2. 单台redis实例要设置多少个maxclients?

3思路:

假设单台redis实例设置maxclients = 20000,而且我们通过压测发现单台客户端最多使用了500个连接,那么可以推断出
理论上,最多只能部署40个客户端;

4详细分析

4.1 说明

针对每个实例创建一个池:maxIdle和maxTotal指的是针对一个redis实例的设置,意思就是如果连接两个实例,那么就真正的池的大小要增加一倍,如果三个,那么就又增加一倍;

4.2连接池不够用,又不能创建

Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
此刻连接池用光了,并且blockWhenExhausted该值为true(该值默认为true),就需要等连接池有可用的,但是等待超时报该错误;

分两种情况解决上面的问题:
1.调大等待时间(如果还是超时,就在上层限流)
2.增加连接池数目(千万不能超过服务端的maxclients限制)

注意:
jedis的如下值
maxIdle:表示池中目前可用的(不代表真的可用);
maxTotal:表示池中能装多少(超过这个池的大小,不会再创建了)
取决于服务端允许多少,如果服务端允许1个,jedis的这两个值设置再大都是无用的;

复现方法:
1.客户端多线程并发调用
2.maxIdle和maxTotal设置为2,服务端maxclients也设置为大于2的值即可();

4.3 连接池用完了,然后新创建的validate失败

Caused by: java.util.NoSuchElementException: Unable to validate object
无论blockWhenExhausted是否为true,都说明连接池用光了,而且也创建了(新的对象并放入池中)。
另外,也激活(activateObject)了该对象(不能激活会报错:Unable to activate object),但是该对象(网络连接)是不可用的(一种可能是服务端最多可连接5个,你这个是第6个连接);
说明:对象激活和验证可用是两个性质的问题,激活不代表真的可用;

因此,针对上面的情况(即不够用采取创建但是此时可能不可用),一种折中的方法是:不创建(来避免这种意外的情况),但是不创建怎么够用,
那么就把天花板(maxTotal)设置的小一点;如果此时,客户端会等待超时,那么就调大点等待超时时间;最好的做法是上层限流(把目前能处理的批次当做一个任务来对待,处理完成了,再接收一个新任务);

复现方法:
1.一台客户端多线程并发调用,把连接吃光,另外一台客户端就拿不到可用的;
2.maxIdle和maxTotal远大于大于2的值,服务端maxclients也设置为2的值即可;

5 压测示例

说明:
* A任务表示对一个设备进行和redis交互操作(一个A任务包含5次redis操作);
* 如下是一个服务 + 一个redis实例

20个线程任务(每个任务分为10000个A任务) + (使用了)10个redis连接 = 1785(qps的值)
10000个线程任务(每个任务分为20个A任务) + (使用了)500个redis连接 = 2531(qps的值)
2000个线程任务(每个任务分为100个A任务) + (使用了)500个redis连接 = 2325(qps的值)
20000个线程任务(每个任务分为10个A任务) + (使用了)500个redis连接 = 2040(qps的值)
200000个线程任务(每个任务分为1个A任务) + (使用了)500个redis连接 = 2083(qps的值) 接近符合我们的业务场景(因为我们的业务场景是一个任务是2000个线程任务)

6 关键代码与分析

说明:要通过报错信息来分析和推导出原因是什么

    public T borrowObject(long borrowMaxWaitMillis) throws Exception {
        this.assertOpen();
        AbandonedConfig ac = this.abandonedConfig;
        if(ac != null && ac.getRemoveAbandonedOnBorrow() && this.getNumIdle() < 2 && this.getNumActive() > this.getMaxTotal() - 3) {
            this.removeAbandoned(ac);
        }

        PooledObject p = null;
        boolean blockWhenExhausted = this.getBlockWhenExhausted();
        long waitTime = 0L;

        while(p == null) {
            boolean create = false;
            if(blockWhenExhausted) {
                p = (PooledObject)this.idleObjects.pollFirst();
                if(p == null) {
                    create = true;
                    p = this.create();
                }

                if(p == null) {
                    if(borrowMaxWaitMillis < 0L) {
                        p = (PooledObject)this.idleObjects.takeFirst();
                    } else {
                        waitTime = System.currentTimeMillis();
                        p = (PooledObject)this.idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
                        waitTime = System.currentTimeMillis() - waitTime;
                    }
                }


                if(p == null) {//此时表明已经超过maxTotal,不能创建;报错:等空闲超时
                    throw new NoSuchElementException("Timeout waiting for idle object");
                }

                if(!p.allocate()) {
                    p = null;
                }
            } else {
                p = (PooledObject)this.idleObjects.pollFirst();
                if(p == null) {
                    create = true;
                    p = this.create();
                }

                if(p == null) {
                    throw new NoSuchElementException("Pool exhausted");
                }

                if(!p.allocate()) {
                    p = null;
                }
            }

            if(p != null) {
                try {
                    this.factory.activateObject(p);
                } catch (Exception var15) {
                    try {
                        this.destroy(p);
                    } catch (Exception var14) {
                        ;
                    }

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

                if(p != null && this.getTestOnBorrow()) {
                    boolean validate = false;
                    Throwable validationThrowable1 = null;

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

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

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

        this.updateStatsBorrow(p, waitTime);
        return p.getObject();
    }

7.性能追求

如果要追求性能,(生产环境)需要将testOnBorrow和testOnReturn设置为false(至于不能用的或者空闲的连接,可以用其他配置或者方式处理,因为连接不可用造成的异常也要捕获进行相应处理),因为进行这两个操作,性能低;

8.总结

涉及到网络连接的开发,都会有连接池;
在出现连接池连接不够用,一般原因的原因可能有如下几种:
1.客户端线程长期占用,不还回池中;
2.服务端处理性能低,一个连接被长期使用和占用;
3.向池子拿的速度比还的快(如果在内网,网络操作还是非常快的,这种情况很少发生,公网需要特殊分析和对待)
在出现连接超时时,一般原因可能有如下几种:
1.服务端的backlog的限制,这个时候要分析连接状态(SYN_SENT、SYN_RCVD、主动断开连接的可能的状态(FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT)、被动断开连接的可能的状态(CLOSED_WAIT、LAST_ACK))是否存在异常(正常情况下,这些状态一般都是比较少的,或者通过netstat看不到的)。
2.客户端的连接也是有限制,这个也需要考虑,方法同服务端检查方法一样;

3.网络情况很差;

9其他

缓存网络对象,参见http://blog.csdn.net/KuaiLeShiFu/article/details/77746857

参考:http://blog.csdn.net/hguisu/article/details/38700899
http://www.cnblogs.com/Toonter/p/5926094.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值