破案现场:记一次压测异常排查--Redisson锁失效的场景
异常现场回顾
7月7日对小驴班级推送视频任务功能进行压测。 压测过程中发现有一部分接口请求失败。 小驴端上报推送失败异常如下:
这个异常日志是小驴端收到的。 异常内容是网校网关返回的异常页面, 如何看出来是网校网关的呢? 看回到首页前面有一个url,这个跳转到的url正是网校的主页
班级任务推送主要是to B的,老师们使用, 并发量并不大,10并发,25次, 一共250个任务。 并发量并不大,到底为什么异常,怎么出的异常呢?
首先,我们来看ELK日志监控。
先来查询网关日志,搜索对内网关,查询条件是:对内网关+请求路径,查看并发情况。
我们看到一共并发请求了207次, 目测都是成功的。 但预计是250次,实际只有207次呢?再来查询异常情况。 如何查询呢? 查询条件:
- project.key is cloudlearn-gateway-inner
- response is not 200
发现问题了,这里有21条请求500的日志。
第二步:进入网关查询异常原因。
根据在elk中失败请求的traceId查询网关日志,燃鹅并没有什么收获。 于是带着这个traceId去业务服务器查找
第三步: 查找业务服务器日志。
业务服务器抛出异常, 获取锁失败。
异常原因分析及解决
回想昨天小驴端异常数据,我们看到有好几个小驴id都抛了异常。这里加了一个redisson锁,这个锁的key是:paperId+lvTaskId+originType. 案例来说,压测的lvTaskId应该都不一样才对呀。 怎么会出现锁住的情况呢。
线上日志不全面,为了能够模拟这个问题,在本地启动jemeter进行压测。测试范围lvTaskId从100000-999999随机生成20个。id重复的概率很小了,很快有效果了
20个请求,有两个请求失败了。复现了压测现场出现的问题。 这就好办了,分析原因:
第一步: 查看本地日志,发现异常日志
和线上压测所报异常一样。
第二步:确定随机生成的lvTaskId是否有重复。
因为Redisson锁的key不允许重复, 如果重复将进行等待, 等待超时,就会抛获取锁失败。排查结果: 没有重复的lvTaskId
复制代码
第三步:排查Redisson源码,在异常处打断点,逐步排查。
@Around("controllerAspect(lock)")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint, Lock lock) throws Throwable {String[] keys = lock.keys();if (keys.length == 0) {throw new RuntimeException("keys不能为空");}String[] parameterNames = new LocalVariableTableParameterNameDiscoverer().getParameterNames(((MethodSignature) proceedingJoinPoint.getSignature()).getMethod());Object[] args = proceedingJoinPoint.getArgs();long attemptTimeout = lock.attemptTimeout();if (attemptTimeout == 0) {attemptTimeout = redissonProperties.getAttemptTimeout();}long lockWatchdogTimeout = lock.lockWatchdogTimeout();if (lockWatchdogTimeout == 0) {lockWatchdogTimeout = redissonProperties.getLockWatchdogTimeout();}LockModel lockModel = lock.lockModel();if (lockModel.equals(LockModel.AUTO)) {LockModel lockModel1 = redissonProperties.getLockModel();if (lockModel1 != null) {lockModel = lockModel1;} else if (keys.length > 1) {lockModel = LockModel.MULTIPLE;} else {lockModel = LockModel.REENTRANT;}}if (!lockModel.equals(LockModel.MULTIPLE) && !lockModel.equals(LockModel.REDLOCK) && keys.length > 1) {throw new RuntimeException("参数有多个,锁模式为->" + lockModel.name() + ".无法锁定");}log.info("锁模式->{},等待锁定时间->{}秒.锁定最长时间->{}秒",lockModel.name(),attemptTimeout/1000,lockWatchdogTimeout/1000);boolean res = false;RLock rLock = null;//一直等待加锁.switch (lockModel) {case FAIR:rLock = redissonClient.getFairLock(getVauleBySpel(keys[0],parameterNames,args));break;case REDLOCK:RLock[] locks=new RLock[keys.length];int index=0;for (String key : keys) {locks[index++]=redissonClient.getLock(getVauleBySpel(key,parameterNames,args));}rLock = new RedissonRedLock(locks);break;case MULTIPLE:RLock[] locks1=new RLock[keys.length];int index1=0;for (String key : keys) {locks1[index1++]=redissonClient.getLock(getVauleBySpel(key,parameterNames,args));}rLock = new RedissonMultiLock(locks1);break;case REENTRANT:rLock= redissonClient.getLock(getVauleBySpel(keys[0],parameterNames,args));break;case READ:RReadWriteLock rwlock = redissonClient.getReadWriteLock(getVauleBySpel(keys[0],parameterNames,args));rLock = rwlock.readLock();break;case WRITE:RReadWriteLock rwlock1 = redissonClient.getReadWriteLock(getVauleBySpel(keys[0],parameterNames,args));rLock = rwlock1.writeLock();break;}//执行aopif(rLock!=null) {try {if (attemptTimeout == -1) {res = true;//一直等待加锁rLock.lock(lockWatchdogTimeout, TimeUnit.MILLISECONDS);} else {res = rLock.tryLock(attemptTimeout, lockWatchdogTimeout, TimeUnit.MILLISECONDS);}if (res) {Object obj = proceedingJoinPoint.proceed();return obj;}else{throw new LockException("获取锁失败");}} finally {if (res) {rLock.unlock();}}}throw new LockException("获取锁失败");
}
复制代码
异常就出在这段代码里,于是仔细阅读这段代码。如何生成key,如何上锁,如何重试。 突然,注意到解析key的代码段
public String getVauleBySpel(String key, String[] parameterNames, Object[] values) {if(!key.startsWith("#")){return key;}//spel解析器ExpressionParser parser = new SpelExpressionParser();//spel上下文EvaluationContext context = new StandardEvaluationContext();for (int i = 0; i < parameterNames.length; i++) {context.setVariable(parameterNames[i], values[i]);}Expression expression = parser.parseExpression(key);Object value = expression.getValue(context);log.info("key={},value={}",key,value);if(value!=null){return value.toString();}return "";
}
复制代码
第一句话:if(!key.startsWith("#")){return key;} 如果redisson的key不是以#开头,那么将不对key的参数进行解析。问题就在这里了。
通常,我们在定义key的时候,会在变量前面加一个常量。可以方便日志排查。如下图:
也正是这个问题,导致了所有并发请求始终都是一把锁。最后出现等待超时的问题。
第四步:修改锁,将常量放在后面
再次压测,结果正常。
总结
redisson锁在设置key的时候,如果加常量, 切忌不可加载前面, 要加在后面, 要是修改源码的话那就另说了。
其实,在redisson-spring-boot-starter文档中也有提说可以使用常量。 一方面这个提示是在注释中写的,另一方面没有明确说常量应该放在那里。对于常用redis且习惯将常量放在前面的我们来说,这确实是一个坑。
至此,这次异常排查结束,记录这次排查过程,希望后面大家再使用Redisson的时候,能够避免趟类似的坑。
作者:summerSunShine
链接:https://juejin.cn/post/7061152891528019981
破案现场:记一次压测异常排查--Redisson锁失效的场景相关推荐
- 记一次压测Feign调用时Hystrix could not be queued for execution and no fallback available.
项目场景: 同事压测时反馈仅支持10个用户的并发量 问题描述 通过查看日志,可以看到一下关键报错信息: could not be queued for execution and no fallbac ...
- 全链路压测自动化实践
背景 境内度假是一个低频.与节假日典型相关的业务,流量在节假日较平日会上涨五到十几倍,会给生产系统带来非常大的风险.因此,在2018年春节前,我们把整个境内度假业务接入了全链路压测,来系统性地评估容量 ...
- 美团全链路压测自动化实践
境内度假是一个低频.与节假日典型相关的业务,流量在节假日较平日会上涨五到十几倍,会给生产系统带来非常大的风险.因此,在2018年春节前,基于美团基础的压测平台Quake,我们把整个境内度假业务接入了全 ...
- 全链路压测自动化实践 1
背景与意义 境内度假是一个低频.与节假日典型相关的业务,流量在节假日较平日会上涨五到十几倍,会给生产系统带来非常大的风险.因此,在2018年春节前,我们把整个境内度假业务接入了全链路压测,来系统性地评 ...
- 微服务:全链路压测和容量规划
什么是全链路压测? 基于实际的生产业务场景.系统环境,模拟海量的用户请求和数据对整个业务链进行压力测试,并持续调优的过程 主要特征: 真实流量 线上环境 实时监控和过载保护 全链路压测组成 单链路指一 ...
- 阿里云性能测试服务 PTS 新面貌 - 压测协议、施压能力全新升级
作者:笛墨 审核&校对:风云 编辑&排版:雯燕 引言 性能测试 PTS(Performance Testing Service)是一款阿里云 SaaS 化的性能测试工具,从最早为了精准 ...
- 压测导致mysql数据库CPU很高_排查压测问题引发的系统性能调优过程
前言:今天测试部门的小梦找到我,委屈巴巴的说我写的接口有问题,因为她对这个接口进行压力测试时,发现系统的吞吐量一直上不去,并且 应用服务器 (部署接口项目的服务器) 的CPU.内存等资源的使用率也一直 ...
- 细说双 11 直播背后的压测保障技术
简介:阿里云 PTS 站在双 11 巨人的肩膀上,是阿里全链路压测的延伸.PTS 通过伸缩弹性,轻松发起用户百万级别的流量,免去机器.人力成本:PTS 对流量的控制,能够实时脉冲,精准控制: 是应对视 ...
- 干货 | 应用性能提升 70%,探究 mPaaS 全链路压测的实现原理和实施路径
简介:全链路压测方案下,非加密场景下至少有 70% 的性能提升,加密场景下 10%的性能提升,并在 MGS 扩容完成后可实现大幅的性能提升,调优的结果远超预期. 业务背景 随着移动开发行业的步入存量时 ...
最新文章
- 《GPU高性能编程CUDA实战》中代码整理
- 使用restTemplate报400或者415错误
- 如何在windows7上安装启明星系统。
- 『设计模式』工厂方法模式
- ARM-Button-Driver-硬件图
- 使用php创建一个注册表单,如何实现一个简单的注册表单
- 别人叫我程序猿,我称自己攻城狮。没日没夜写代码,不知何日涨工资?
- Leetcode刷题系列汇总
- XP命令合集(开始→运行→输入的命令集锦开始→运行→输入的命令集锦)
- Python3.6 所有内置函数
- PHP 遍历文件夹及文件类及处理类
- iOS开发初学者需要经常去的论坛或网站
- cad文本改宋体字型lisp_cad多行文字如何批量修改样式?
- 笔记本计算机没反应怎么办,有办法 | 耳机插进电脑没反应怎么办?
- Unity Keyword
- c莫比乌斯函数_莫比乌斯函数
- 深入探索JVM垃圾收集器 — 经典垃圾收集器之Parallel Scavenge收集器、Serial Old收集器、Parallel Old收集器
- 计算机专业素质拓展,创新与素质拓展学分.doc
- 基于单片机的RFID考勤刷卡电路设计(#0207)
- Apollo决策技术分享20190328
热门文章
- 【无标题】IBM X3500服务器故障|前面板指示灯...
- ununtu14安装ipset-6.24
- MYSQL数据分析项目 - 淘宝用户行为分析
- 关于CMSIS-DAP
- 北京十大情人分手圣地
- html 隐藏元素点击事件,css隐藏元素的几种方法中可以触发点击事件的是?
- MySQL服务无法启动(2003 - Can‘t connect to MySQL server on‘localhost‘
- VDMA学习(一)pg020总结
- html百度地图中心点不正确,关于网页调用百度地图定位不准的问题?
- FCM聚类算法详解(Python实现iris数据集)