技术干货 | 高负载压测下接口异常问题定位排查-Redis

Posted MobTech技术君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了技术干货 | 高负载压测下接口异常问题定位排查-Redis相关的知识,希望对你有一定的参考价值。

背景:

xx业务接口主要为获取全国范围地区信息,真实生产场景是调用频繁数据量大,因此需要对该业务接口做性能测试,确认接口性能及高负载下承受能力。

接口处理逻辑:获取全国范围地区信息,第一次从mysql获取信息,获取到信息后hset到redis,后面的获取信息都走redis获取并返回接口数据。

 

问题:

20并发对该接口进行持续加压,压力负载不断提升,压力机端监控到返回错误信息,连接失败(10并发正常),

应用服务抛出异常:getList:merchant:area:list error

redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

 

定位排查:

从应用抛出异常信息看出,无法获取更多的redis线程池资源,但20并发还未造成高强度的压力,进一步排查:

硬件资源:服务器资源利用率正常,cpu、内存,磁盘等比较充足,排查资源影响

 

Redis配置及性能:查看redis连接池配置redis.pool.maxIdle=300,redis.pool.maxTotal=600,设置足够大,在20并发持续压测下,该最大连接数已足够大,但仍然抛出redis连接池异常,应存在其他方面因素影响,继续排查

redis连接数正常,netstat -nap |grep redis |wc -l,100多个活动连接。

redis -info查看redis信息连接正常,正常连接100多个。

redis -monitor获取数据正常,get和hget数据均正常。

 

查看redis日志,找到问题如下问题:

WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add \'vm.overcommit_memory = 1\' to /etc/sysctl.conf and then reboot or run the command \'sysctl vm.overcommit_memory=1\' for this to take effect.

 

优化解决:

从reid日志报错信息看出,需调节linux内核参数

vim /etc/sysctl.conf ,增加vm.overcommit_memory=1,然后sysctl -p 使配置文件生效

 

备注:内核参数vm.overcommit_memory代表内存分配策略,取值为0、1、2:

  1. 表示内核将检查是否有足够的可用内存供应用进程使用;

    如果有足够的可用内存,内存申请允许;

    否则,内存申请失败,并把错误返回给应用进程。

    1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。

    2, 表示内核允许分配超过所有物理内存和交换空间总和的内存

 

相关问题信息:

① 当jedispool中的jedis被取完 等待超过你设置的 MaxWaitMillis 就会抛出Could not get a resource from the pool

 

从GenericObjectPool 源代码borrowObject(long borrowMaxWaitMillis)方法可以看出

 

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; //等待时间超过 borrowMaxWaitMillis 的时候 p =null

}

}

 

if(p == null) {

throw new NoSuchElementException("Timeout waiting for idle object"); 抛出异常 被pool这个类捕捉

}

 

pool 源码

try {

return this.internalPool.borrowObject();

} catch (Exception var2) {

throw new JedisConnectionException("Could not get a resource from the pool", var2);

}

 

所以只要把jedis配置MaxWaitMillis 设置的大一点 就可以降低 由于MaxWaitMillis 导致的

Could not get a resource from the pool ,设置太大会造高负载并发下硬件性能的大量开销,可根据压测目标并发数据得到合理的参数设置,及达到较好性能也不至于服务器资源过度消耗

 

②、加快从jedispool中获取get jedis 和return jedis的 速度

 

设置 testOnBorrow、testOnReturn 都改为false

在这两个配置为true的情况下 get 、 return jedis的时候 jedis 将ping 一下redis。

从GenericObjectPool 源代码borrowObject(long borrowMaxWaitMillis)方法可以看出:

 

if(p != null && (this.getTestOnBorrow() || create && this.getTestOnCreate())) {

boolean validate = false;

Throwable validationThrowable1 = null;

try {

validate = this.factory.validateObject(p);

获取直接先验证是否可以用

} catch (Throwable var13) {

PoolUtils.checkRethrow(var13);

validationThrowable1 = var13;

}

JedisFactory 源代码validateObject(PooledObjectpooledJedis)方法可以看出

BinaryJedis jedis = (BinaryJedis)pooledJedis.getObject();

try {

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

} catch (Exception var4) {

return false;

}

 

基本以上这么修改 就能够解决Could not get a resource from the pool

操作jedis 的时候 设置 testOnBorrow、testOnReturn 都改为false ,要比true 快上1.4倍,但可能带来的问题可进一步研究。

以上是关于技术干货 | 高负载压测下接口异常问题定位排查-Redis的主要内容,如果未能解决你的问题,请参考以下文章

linux 排查cpu负载过高异常

linux 排查cpu负载过高异常

压测过程中故障排查之一:高CPU占用问题分析案例

破案现场:记一次压测异常排查--Redisson锁失效的场景

Nginx 高并发下报错 connect() failed (110: Connection timed out) while connecting to upstream

#yyds干货盘点# nginx代理后端报502