记一次内网系统首页优化
背景:公司内网招聘系统首页出现监控报警,在排查过程中发现接口比较慢,导致大量请求被抛弃,同时框架层面会抛出异常,因此进行排查和优化
排查过程:
1、看代码
DAO实现层没啥毛病,没有多余的数据库请求,SQL也不复杂(基本算单表查询),数据量不到2500
Service层有两个封装的方法,是在DAO层返回的数据集循环调用的,一个是访问数据库的,有9次访问别的表,
另一个是访问RCP接口的,用来获取k对应的value信息,有8次访问,这8次是分别取不同类型的数据,比如城市,品类,业务线,职级等枚举类型
2、看系统首页
咨询了下之前做过这个系统的同事,说出现异常的HTTP接口是招聘系统首页,因此在线上看招聘系统首页发现前端有很多重复的请求,咨询了前端之后发现首页一页是60条数据,但是前端分12次请求的,前端同学说一开始开发的时候后端接口比较慢,大概60条数据需要5-6s,所以前端采用了分12次请求,并行获取数据。
上面也就解释了为啥有监控报警和后端服务调用量太多,导致队列任务超时出现框架层面的异常了。
可以粗略估算一下,一次刷新页面后端循环总体请求是17*5*12= 1020次
下面开启了优化之路:
1.将调用RCP的数据集封装为一个查询Bean,PositionSearchBean,属性基本为Map类型,用来装RCP调用返回的数据集
2.由于封装的代码被多个地方调用因此需要一个公共服务PositionSearchHelpService,生成PositionSearchBean
3.替换循环调用
4.其他调用同样代码的地方都改成循环调用之前先获取数据集对象
开始按上面的思路重构代码,
但是这个过程中出现一个问题,也就是说分12次调用的时候需要生成12次数据集,此时性能大概提高100ms,这样还可以进一步优化,可以加缓存,
这时候有两种方式1.引入redis 2.guava的缓存模块
但是这两种都抛弃了,1.数据集来源多个RCP服务,且数据可能有变化,数据量比较大,格式不一样2.时间相对紧张,且引入了复杂性
这里介绍一个取巧的方式:在PositionSearchHelpService服务里自定义个全局Map,定义一个key,value 即是数据集PositionSearchBean对象,PositionSearchBean里面保存了当前时间戳,每次获取的时候将当前时间与PositionSearchBean保存的时间戳做对比,即可满足缓存,如果超过一定时间则重新缓存,考虑到用户的一次搜索+定位大概需要10-20秒,则我们可以定义缓存超时时间为30秒,或者更短(一开始是3s)。
由于当前阶段前端还是12次请求,也就是并发请求,所以缓存不能多次执行,否则同样会无法达到优化目标,这时候就需要控制并发,这里使用了synchronized锁住整个方法(解决这个问题的过程还是有些曲折~~~~)
再次测试则可以保证一次刷新12次请求只会有1次缓存初始化,到这里性能大概提高到200-250ms,原来是400-600ms(每次请求),现在是300-450ms(每次请求)
经过测试,忽然发现有些请求其实是不稳定的,有时候一开始刷新会达到1s的时间,有时候12次中有6次或者3次是200到300ms,另外6次50ms左右,
这里简单分析一下原因,1.由于当前是12次请求,请求的时候有锁竞争,同时存在缓存失效的情况,2.当前只优化了8个RCP服务和一个DB循环调用,剩下还有8个DB循环调用没有处理,
现在想来第一个其实不太可能是主要因素,因此比较明显的是需要确定首页列表是否需要8个DB循环调用的职位关联数据集,跟前端沟通后,不需要,
则进一步优化,在PositionSearchBean对象里加needReferData属性标识是否需要查询职位相关连的数据集(7个DB循环调用),由于处理这些循环调用的方法有两个,封装好了,且被多个服务接口引用,因此,需要在有问题的这个接口里面设置为false,其他接口里面设置为true,到这里就可以保证不会影响其他模块,同时去除所有循环调用DB+RCP服务的情况了,
此时再进行测试则一次页面刷新(12次并发调用)后端统计每次调用不会超过30ms,如果缓存失效则那一次调用会达到250-300ms,也就是说基本达到优化预期了。
最后前端将12次调用改为一次调用,即一次获取60条数据,再看后端耗时,如下图:
也就是说到现在循环调用基本被消除,从原来的耗时5s左右,到现在不到50ms~~~~~~
前端说优化的可以,本来预计让前端分2-3次取60条数据呢,现在一次60条不成问题......
总结:出现这种情况的原因就是for循环中调用了多次db+RCP服务,同时前端也做了一定的推波助澜,并发调用,导致服务出现崩溃的报警
1.首页数据展示,后端可根据情况访问主表相关的关联表数据,这个情况就是前端在首页数据展示的时候不需要,后端多余的进行了DB加载
2.上面的优化结果的基础就是多次RCP服务是获取并不经常更新的k-v数据集,所以首页查询的时候可以用上缓存策略。