文章目录
-
-
- 压测原因
- 压测分析
-
- 第一次排查
- 第二次排查
- 第三次排查
- 第四次排查
- 问题解决
-
压测原因
首页改版,所以要对首页进行压测,首页展示过程中涉及多个接口,需要进行压测。
压测分析
第一次排查 简单查看压测请求情况,初步观察日志可以额看到十个中有一到两个请求出现几百甚至上千毫秒的接口相应,排查TraceID之后发现为:接口调用到普通的测试环境和性能环境,导致部分请求走到测试环境,普通的测试环境只有1C2G,而生产环境的配置为40核64G/92G,所以相应结果较长也在意料之中。
同时排查过程中还发现部分接口调用链极短,这与线上的请求调用是不相符的,排查发现是请求头Header未设置数据导致很多请求被过滤到,所以在请求头上加入对应参数再次压测。
第二次排查 第二次压测过程,惨不忍睹,整体请求成功率只有76%左右,查询部分接口的响应时间很长,甚至直接溢出500,可以看到product服务的(ProductLimitFacade.queryProductLimitByXXX)由于接口请求时间过长,导致sofa线程池被打满,从而出现性能测试曲线出现急剧下降以及请求报错。
- TPS断崖下跌
- 请求时长由毫秒级\十几毫秒增长到上千毫秒,打满清空多次成正弦曲线状态
排查SQL日志过程中,也可以看出有大量的SQL查询日志,导致接口相应太慢,大量线程阻塞在查询限额的方法出,所以目标锁定–代码的XXCache缓存使用不当,导致缓存策略未生效。
(XXCache:内部封装的一种缓存策略,根据参数值可以设置缓存在Redis或者缓存在本地中,可以参考GuavaCache的实现方式)[参考:GuavaCache的认识]
@XXCache(value = "https://www.it610.com/article/productLimit", key = "productYieldDXXX_#{}_#{}_#{}")
第三次排查 现在已知是缓存策略失效的问题,第一反应为注解使用不当,导致缓存穿透。因为同时查询产品限额信息,以为是未配置allowNullValue的问题导致的,但是XXCache会默认会开启空缓存数据,所以也不是缓存穿透导致的。[参考:缓存穿透、缓存击穿、缓存雪崩区别和解决方案]
- 缓存的正常流程为:先查询缓存然后查询数据库。查询缓存,若存在数据,则返回;如果缓存没有则查询数据库;如果数据库也没有返回空。
- 缓存穿透:缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
- 缓存雪崩:缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
回归代码本源,接口调用的是列表查询接口,但是真正的本地缓存的是单个产品限额查询的接口,出发点很好,毕竟产品编号列表的排列组合太多,会不必要的浪费服务器宝贵的内存资源,问题就出在for循环的this调用,我们知道Spring的AOP核心思想是基于动态代理来实现的,如:
- 基于接口的JDK的InvocationHandler动态代理
- 基于子类继承的CGLib的MethodInterceptor动态代理。[Spring AOP 动态代理]
问题解决
解决办法也很简单:一种是移除this调用改为获取代理对象(实现并注入ApplicationContextAware;然后使用该上下文来获取this服务的代理对象)来执行即可拦截增强,另一种将XXCache注解下移,将注解加到Manager上即可完成。
【11_性能测试|性能分析(动态代理失败导致缓存策略失效)】参考:
- 缓存那些事儿
- 动态代理与静态代理的区别
- GuavaCache的认识
- Spring AOP 动态代理
推荐阅读
- 面试复习|敖丙思维导图-Redis
- 代理模式|SpringIOC 和AOP 的理解
- SpringMVC|Springboot 设置上传文件大小
- typescript|6.typescript类
- tcp流量控制和拥塞控制
- R语言|R 语言读写文件
- python|python经典案例(2)
- JAVA|Java(开始Java编程生涯的小指南)
- java|[多线程] | 实例演示三种创建多线程的方式,初识线程同步以及解决线程安全问题(超卖)