2021-08-18 06:25:56 版本 : 登录接口压力测试问题分析-续
作者: 潘帅 于 2021年08月17日 发布在分类 / 人防组 / 人防后端 下,并于 2021年08月18日 编辑
 历史版本

修改日期 修改人 备注
2021-08-18 16:20:37[当前版本] 潘帅 1.2
2021-08-18 06:25:56 潘帅 1.1
2021-08-17 22:46:02 潘帅 1.0

经过上次的问题分析,对登录接口进行了一波调整,并发情况下接口稳定性提高了,不会出现报错的情况,但速度还是满足不了要求。

并发100线程快的话二十几秒,慢的话两分多钟,经常出现诡异的卡顿。要了老命了,一波分析之后还是没根本上解决问题,这不是啪啪打脸白分析了么。

数据库性能?除了第一次请求后续请求都是读取Redis缓存,不至于这么拉跨

操作流程?已经两步并作一步了,貌似不能再缩减了

网关组合Ocelot加Consul的锅?确认过配置文件,并没有问题

到这里似乎陷入了绝境,头发又掉了几根,然鹅天无绝人之路,跟兄弟部门的同事沟通后发现排查问题的思路都是一致的,也许是漏了什么。

再来一遍!


1.问题排查

总体架构或者请求的流程是:前端发起请求,网关服务Gateway转发请求,授权验证服务Admin处理请求后原路返回。按照这个流程设计了几个场景来排查问题。

1.1直接访问授权验证服务Admin的接口

同样是并发100,不经过网关Gateway,直接访问授权验证服务Admin的接口。

不到1秒跑完,快到飞起,不敢再说是授权验证服务Admin的问题了。


1.2授权验证服务Admin的接口不做业务处理直接返回

注释掉授权验证服务Admin接口中的所有业务逻辑,直接返回200,应该没有比这更快了吧

并发100,跑起.....卡死.....大约两分钟跑完。原来叛徒在这里,网关服务Gateway的问题没错了。


2.逻辑分析

问题就在网关这里,网关服务引用了Ocelot组件,并配合Consul使用。都属于常规操作,跟网上的大部分示例都一致,配置文件也没找到毛病。配置文件修改后结果也没有发生很大变化,可以排除Ocelot和Consul的嫌疑了。

这时接口重定向的代码逻辑成功引起了我的怀疑,之前也怀疑过它,但由于它还算能正常工作也没多想。现在看来这部分逻辑确实值得探讨,来看看它的逻辑。

粘贴图片

粘贴图片

应用服务类Login_OAuth为开发给外部的接口,在顶部实例化Realize类,通过Realize实例来进行实际接口的跳转。同时也对传递的参数进行转换。

粘贴图片

在Realize实例的GetOAuthOfAdmin方法中才是真正实现接口跳转的地方。核心代码就是iAdmin.GetAdminOfOAuth。

iAdmin是接口IAdmin的实例,接口实例是在Realize实例的开始阶段获取。

粘贴图片

首先是获取一个数据对象SignDto,再从SignDto中解析IAdmin的实例。

对象SignDto的实例化过程比较复杂,从代码的执行逻辑来看,既包含了IAdmin接口的注册(将IAdmin接口注册为HttpApi),也包含了IAdmin实例的解析(解析出当前实例化的HttpApi对象)

粘贴图片

另外就是SignDto内包含了一个线程共享的单例对象,用于读取存储应用基本配置,包含应用Id、应用key和租户id。

从代码逻辑可以看出当时的设计思路:线程共享的单例对象SignDto包含应用配置信息、HttpApi接口的注册和解析,既能在线程间实时更新和共享配置信息,也能控制要注册的接口和要解析的实例,从而动态的实现Http请求。

单次运行这个逻辑并没有问题,运行也是正常的。但是遇到并发请求时,问题就来了。Realize类的实例化、SignDto的实例化、HttpApi接口的注册、HttpApi实例的解析在每次请求都会执行一遍,不断的装箱拆箱势必耗费不少时间。

另外SignDto的单例对象为了避免线程抢占锁死使用了volatile关键字,然而从查询资料的情况来看这个关键字并不能达到期望的效果。


其实网关这一堆逻辑的核心只有一个,在Gateway接口内部创建Http请求访问授权验证服务Admin接口,也就是请求套请求。通过一系列的接口是实现了网关服务与接口约定的解耦,性能却也不可避免的降低了。


直接点,替换成独立的Http请求也许是个不错的选择。(HttpApi接口在服务启动时只注册一次)


3.性能分析

再来个深度性能分析,顺便看看底层的堆栈使用情况。请出微软性能分析神器PerfView。

粘贴图片

点击“Collect>Collect”,可以看到采样界面。

粘贴图片

可以将CPU的时钟采样间隔改为0.125,这样能得到更详细的采样结果。点击“Start Collection”按钮开始采集。同时启动并发请求,记录并发请求期间的堆栈调用情况。并发请求结束后停止采样,PerfView会自动压缩采样日志文件。

粘贴图片

PerfView采集的日志还是很全面的,包括CPU栈、进程栈、磁盘IO栈、内存栈等。双击“CPU Stacks”可以看到采集到的所有进程,由于结果记录的计算机的所有集成,只筛选需要的两个进程GatewayHost和RunGo.Core.Web.Host(即Admin服务进程)。

粘贴图片

选择“Flame Graph”查看这两个进程CPU资源占用的火焰图。可以看出在采样时间段内,两个进程在多并发条件下使用多线程进行处理,每个线程所消耗的时间能很清楚的看到。

也可以将PerfView的采集日志导出为speed scope类型的json文件。通过speedscope网站(https://www.speedscope.app/)打开可以看到更详细的火焰图。speedscope的火焰图是倒着看的。

找一个操作时间比较长的线程看看它在干啥。

粘贴图片

似乎能看出来什么问题,但似乎又看出来有什么问题。


-----------------------------------------------------------------------------------------------------------------------------

附:PerfView下载

https://github.com/microsoft/perfview/releases



附:火焰图怎么看

火焰图是基于 perf 结果产生的 SVG 图片,用来展示 CPU 的调用栈。
y 轴表示调用栈,每一层都是一个函数。调用栈越深,火焰就越高,顶部就是正在执行的函数,下方都是它的父函数。
x 轴表示抽样数,如果一个函数在 x 轴占据的宽度越宽,就表示它被抽到的次数多,即执行的时间长。注意,x 轴不代表时间,而是所有的调用栈合并后,按字母顺序排列的。
火焰图就是看顶层的哪个函数占据的宽度最大。只要有"平顶"(plateaus),就表示该函数可能存在性能问题
颜色没有特殊含义,因为火焰图表示的是 CPU 的繁忙程度,所以一般选择暖色调。

PerfView自带的火焰图是正向的,SpeedScope的火焰是倒着的。


顺便看看授权验证服务Admin在并发情况下某个现场的执行过程。

粘贴图片



 附件

附件类型

RARRAR PNGPNG

历史版本-目录  [回到顶端]
    知识分享平台 -V 4.8.7 -wcp