随用户增加、业务迭代,系统面临各种挑战,如不及时优化,会诸多问题:系统越来越慢,流量一高系统就卡顿甚至宕机。性能优化贯穿软件生命周期。
完成某一功能所需要的时间,“平均响应时间”、“百分位数”等指标衡量。
接口平均处理能力。
计算方式:接口请求所有的响应时间和 / 总请求数。
超过n%的请求都在m时间内返回,一般用TPn=m描述,如:TP99=5,表示超过99%的请求都能在5ms内返回。
计算方式是:将接口的响应时间按从小到大的顺序进行排列,取特定百分位的耗时,即为该接口的百分位数。
百分位数更反映接口整体响应情况,因为高并发场景常出现长尾请求,如用平均响应时间衡量,由于长尾请求会被大量低RT平均掉(此时很多用户的请求已经很慢了),进而无法及时感知真实业务状况。举个例子:接口A有100次请求,其中97次1ms,3次100ms,平均响应时间为 (1 * 97 + 3 * 100) / 100 = 3.97ms,此时3.97ms并不能真实反映接口的整体性能,因为其中97次请求RT才1ms。
一般用QPS或TPS衡量:
性能优化本质上也是从“优化时间”、“优化空间”、“时空互换(用时间换空间或用空间换时间)”思考,在空间、时间取舍。
限速50km/h,规定任何时刻车道有且仅有一辆。1h内,从A点出发到达B点的汽车最多只有10辆。
提升这块路段车流,咋办?
增加车道数(空间维度):如增到6车道,1h内从A点出发到达B点的汽车数可提到60:
第二种方式,道路提速(时间维度):提到100km/h,那么,在1h内,从A点出发到达B点的汽车数可提到20:
人维:性能优化是属于技术团队的,技术团队包括开发、测试和运维:
产品维:性能优化是业务功能的一部分,是为了满足某些业务场景。性能优化考虑:
结合性能优化的本质,整个优化过程:先从业务需求角度出发,思考待优化场景是否值得投入,如一个任务每次需要跑半小时,从技术层面,可以做下优化,但结合业务情况却发现,此任务的执行频次是每周一次,如果优化此场景需要耗费较大人力,那么,这个投入就是不值得的;
技术实现角度,思考咋优化时间、空间、牺牲空间换时间、牺牲时间换空间等。总之,需要以一个更全面的视角去看待它,避免进入头痛医头脚痛医脚。
一个外部请求进入系统,会经历多个软硬件节点,所有节点处理时间之和才是用户请求处理时间,如其中任意一个节点性能有问题,系统整体性能就上不去。由于节点自身差异性,性能提升方法也不同,但总体分为:
一个外部请求进来后,让其在尽可能短的时间内处理完成。
技术角度:
业务角度:
单个请求拆为多个子请求,各子请求并行处理,子请求结果合并后返回。可基于 CompletableFuture 实现一套并行处理框架,运用到如商品详情页加载。
最典型的MQ如:下单操作时,除了扣减库存、生成订单外,还给用户发送支付成功消息、赠送积分等后置操作。非核心后置流程采用MQ异步处理,提升下单接口性能。
进程内,另开一个线程执行这些非核心流程;或先将非核心操作数据暂存在某种介质(DB表、redis等)中,然后采用定时任务定期扫描并执行这些操作。
有多个外部请求进来时,让系统内部多个节点分别处理这些请求,或者节点内部做并行处理。
如节点采用集群部署,并通过LB,将用户请求分摊到不同的节点进行处理;节点内部采用线程池。
直播间进入:用户先访问直播商品详情页,然后购买,再访问详情页面时,会出现直播间入口,在进入直播间之前,会做一次权限校验,校验通过后,才可进入直播间互动:
外部请求:
通过流量监控数据及日志分析,发现性能瓶颈在“直播商品详情加载”。商详因为上游服务请求量超过下游服务承受吞吐量,导致大量RPC调用超时。
反映出问题:
对此,主要优化方向如下:
优化前,梳理整个调用链上,接口强弱依赖关系及每个接口RT
统计出弱依赖接口 TP99(RT较稳定的接口)/ TP95 (RT波动较大接口)的RT,设置它们的超时时间为 (1 + 50%) (TP99 或 TP95)
一般设置超时时间为2s或3s,但每个接口的RT是不一样的,比如:接口A的RT稳定在100ms内,那么,如果超时时间是2s,假若接口A超时了,本次RT至少是2s,但如果超时时间设置为100ms,且我们加了1次重试,那么,本次请求的RT不会超过200ms,同时,重试时接口很大概率会正常返回结果。
给接口添加前置缓存。采用分布式缓存,缓存更新策略:采用两个缓存,缓存A、B(缓存A TTL=m分钟,缓存B TTL=n分钟,且n>2m),先从缓存A读,有则返回,无则B读,并在返回前,异步启动一个更新线程,同时更新缓存A和缓存B。
接口接入熔断降级机制,并对异常做捕获,返回默认值。
统计出强依赖接口 TP99 的RT,设置它们的超时时间为 (1 + 50%) (TP99)
根据接口RT波动性,基于dubbo的重试机制,设置重试次数为2或3次。
商品详情页面是聚合类信息展示窗口,除商品基础信息外,还包括A、B、C等内容,且这里的A、B、C和商品基础信息四者间无前后依赖。
将商品详情加载拆分为了4个子任务,并采用并行处理框架,对子任务做了并行化处理,并聚合返回,较大提升接口RT性能。
下游服务方提供稳定的原子化接口。上游调用方使用了下游不太合适的接口。由于历史原因,当前下游服务方中有特别多的查询类接口,且很多查询类接口在功能上都是重叠的。
针对查询类接口,按照其返参字段使用场景的不同,提供三种不同粒度的通用类原子化接口,之后所有的查询类需求,都会强制要求上游调用方从这三类接口中选择:
产品功能是持续迭代的,性能优化不是一蹴而就。针对同一个节点,在不同的时刻,其优化点也可能不一样,如新功能刚上线时,查询性能提升可能仅仅通过加索引的方式便能解决,但随着功能叠加,后续的优化方向可能是“尽量走批量查询”、“加缓存”。因此,性能优化遵循“具体问题,具体分析”。