异常现场回顾

7月7日对小驴班级推送视频任务功能进行压测。 压测过程中发现有一部分接口请求失败。 小驴端上报推送失败异常如下:

这个异常日志是小驴端收到的。 异常内容是网校网关返回的异常页面, 如何看出来是网校网关的呢? 看回到首页前面有一个url,这个跳转到的url正是网校的主页

班级任务推送主要是to B的,老师们使用, 并发量并不大,10并发,25次, 一共250个任务。 并发量并不大,到底为什么异常,怎么出的异常呢?

首先,我们来看ELK日志监控。

先来查询网关日志,搜索对内网关,查询条件是:对内网关+请求路径,查看并发情况。

我们看到一共并发请求了207次, 目测都是成功的。 但预计是250次,实际只有207次呢?再来查询异常情况。 如何查询呢? 查询条件:

  1. project.key is cloudlearn-gateway-inner
  2. 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锁失效的场景相关推荐

  1. 记一次压测Feign调用时Hystrix could not be queued for execution and no fallback available.

    项目场景: 同事压测时反馈仅支持10个用户的并发量 问题描述 通过查看日志,可以看到一下关键报错信息: could not be queued for execution and no fallbac ...

  2. 全链路压测自动化实践

    背景 境内度假是一个低频.与节假日典型相关的业务,流量在节假日较平日会上涨五到十几倍,会给生产系统带来非常大的风险.因此,在2018年春节前,我们把整个境内度假业务接入了全链路压测,来系统性地评估容量 ...

  3. 美团全链路压测自动化实践

    境内度假是一个低频.与节假日典型相关的业务,流量在节假日较平日会上涨五到十几倍,会给生产系统带来非常大的风险.因此,在2018年春节前,基于美团基础的压测平台Quake,我们把整个境内度假业务接入了全 ...

  4. 全链路压测自动化实践 1

    背景与意义 境内度假是一个低频.与节假日典型相关的业务,流量在节假日较平日会上涨五到十几倍,会给生产系统带来非常大的风险.因此,在2018年春节前,我们把整个境内度假业务接入了全链路压测,来系统性地评 ...

  5. 微服务:全链路压测和容量规划

    什么是全链路压测? 基于实际的生产业务场景.系统环境,模拟海量的用户请求和数据对整个业务链进行压力测试,并持续调优的过程 主要特征: 真实流量 线上环境 实时监控和过载保护 全链路压测组成 单链路指一 ...

  6. 阿里云性能测试服务 PTS 新面貌 - 压测协议、施压能力全新升级

    作者:笛墨 审核&校对:风云 编辑&排版:雯燕 引言 性能测试 PTS(Performance Testing Service)是一款阿里云 SaaS 化的性能测试工具,从最早为了精准 ...

  7. 压测导致mysql数据库CPU很高_排查压测问题引发的系统性能调优过程

    前言:今天测试部门的小梦找到我,委屈巴巴的说我写的接口有问题,因为她对这个接口进行压力测试时,发现系统的吞吐量一直上不去,并且 应用服务器 (部署接口项目的服务器) 的CPU.内存等资源的使用率也一直 ...

  8. 细说双 11 直播背后的压测保障技术

    简介:阿里云 PTS 站在双 11 巨人的肩膀上,是阿里全链路压测的延伸.PTS 通过伸缩弹性,轻松发起用户百万级别的流量,免去机器.人力成本:PTS 对流量的控制,能够实时脉冲,精准控制: 是应对视 ...

  9. 干货 | 应用性能提升 70%,探究 mPaaS 全链路压测的实现原理和实施路径

    简介:全链路压测方案下,非加密场景下至少有 70% 的性能提升,加密场景下 10%的性能提升,并在 MGS 扩容完成后可实现大幅的性能提升,调优的结果远超预期. 业务背景 随着移动开发行业的步入存量时 ...

最新文章

  1. 《GPU高性能编程CUDA实战》中代码整理
  2. 使用restTemplate报400或者415错误
  3. 如何在windows7上安装启明星系统。
  4. 『设计模式』工厂方法模式
  5. ARM-Button-Driver-硬件图
  6. 使用php创建一个注册表单,如何实现一个简单的注册表单
  7. 别人叫我程序猿,我称自己攻城狮。没日没夜写代码,不知何日涨工资?
  8. Leetcode刷题系列汇总
  9. XP命令合集(开始→运行→输入的命令集锦开始→运行→输入的命令集锦)
  10. Python3.6 所有内置函数
  11. PHP 遍历文件夹及文件类及处理类
  12. iOS开发初学者需要经常去的论坛或网站
  13. cad文本改宋体字型lisp_cad多行文字如何批量修改样式?
  14. 笔记本计算机没反应怎么办,有办法 | 耳机插进电脑没反应怎么办?
  15. Unity Keyword
  16. c莫比乌斯函数_莫比乌斯函数
  17. 深入探索JVM垃圾收集器 — 经典垃圾收集器之Parallel Scavenge收集器、Serial Old收集器、Parallel Old收集器
  18. 计算机专业素质拓展,创新与素质拓展学分.doc
  19. 基于单片机的RFID考勤刷卡电路设计(#0207)
  20. Apollo决策技术分享20190328

热门文章

  1. 【无标题】IBM X3500服务器故障|前面板指示灯...
  2. ununtu14安装ipset-6.24
  3. MYSQL数据分析项目 - 淘宝用户行为分析
  4. 关于CMSIS-DAP
  5. 北京十大情人分手圣地
  6. html 隐藏元素点击事件,css隐藏元素的几种方法中可以触发点击事件的是?
  7. MySQL服务无法启动(2003 - Can‘t connect to MySQL server on‘localhost‘
  8. VDMA学习(一)pg020总结
  9. html百度地图中心点不正确,关于网页调用百度地图定位不准的问题?
  10. FCM聚类算法详解(Python实现iris数据集)