本文字数:4576字
预计阅读时间:20分钟
01
背景
无论是iOS
还是Android
系统的设备,在线上运行时受硬件、网络环境、代码质量等多方面因素影响,可能会导致性能问题,这一类问题有些在开发阶段是发现不了的。如何在线上始终为用户提供一个相对顺畅的用户体验,是客户端开发需要考虑的一个问题。
02
服务降级、熔断
服务端有降级机制和熔断机制,在设计客户端降级系统时可以参照服务端现有方案。例如发生性能问题或网络拥堵的情况,需要减少设备和网络的负担,等恢复后再进行策略升级。
服务端降级机制,当服务端出现整体负载较大,或因为特殊原因出现数据错误,则会触发降级。不同的情况对应不同的降级策略。例如数据原因导致的,可以不去读DB
数据库,直接返回缓存数据。从用户的角度来看,可能是数据更新不及时,但可以正常显示。
服务端熔断机制,熔断机制是比降级更严重的情况,当服务端中某个微服务不可用,或响应时间过长,则会触发熔断,不再调用这个服务。从用户的角度来看,可能是头像不能显示,或者页面部分模版未显示,购物车商品结算不能使用优惠券等。
03
方案简述
首先,我们需要捋清楚,客户端需要处理的问题都有哪些。我将其分为两类,性能和网速,性能又可以细化为CPU
、内存、电量三类,这三类都会对App
的运行造成影响。同样的,我们不能直接把性能分为好和坏两种,而是需要通过枚举的方式,将其细化为不同等级。
这里以iOS
系统为例,我们需要对iOS
设备CPU
、内存、电量、网速进行实时监控,可以设定一个合理的间隔区间。在发生前面的性能问题时,通过对不同类型的问题进行阈值计算,从而得出对应的等级。如果级别发生变化,则通过通知的方式,告诉业务方降级或升级。
当发生降级时,业务方进行对应的降级处理,例如降低网络请求的图片尺寸。通过业务降级处理,降低系统性能消耗,让CPU
、内存逐步恢复到正常区间,再进行业务升级,恢复原有业务处理规则。
通过上述方式,来保证发生性能或网络问题时,用户依然可以较为流畅的使用App
,并且App
内功能的正常使用不受影响。
04
整体设计
动态降级系统的设计,主要分为三个部分,职责划分如下。
DynamicLevelManager
:调用monitor
和decision
完成分级计算,当级别发生变化时,通过通知的方式告知业务方。
DynamicLevelMonitor
:监控关键性能指标,由manager
定时调用。
DynamicLevelDecision
:由manager
将收集到的性能指标交给decision
,desicion
对于指标进行统一计算,并决定性能级别,并返回给manager
。
demo
代码也可以直接跑起来,如有需要可以直接
copy
拿去用。
05
DynamicLevelManager
DynamicLevelManager
为动态降级系统的核心类,后面都称为manager
,当App
启动时通过openLevelAnalyze
方法注册监听,从而开启一个由dispatch_source_t
实现的loop
,每隔1.5
秒执行一次,执行时会触发dispatch_source_set_event_handler
的回调方法。dispatch_source_t
由手机硬件时钟触发,不受主线程卡顿影响,监听相对精确很多。
/// 开启动态降级监控系统
- (void)openLevelAnalyze {
self.sourceHandle = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(self.sourceHandle, dispatch_time(DISPATCH_TIME_NOW, 0), 1.5 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(self.sourceHandle, ^{
/// 计算综合性能级别
CGFloat cpuUsageValue = [[DynamicLevelMonitor sharedInstance] cpuUsageForApp];
NSInteger memoryUsageValue = [[DynamicLevelMonitor sharedInstance] useMemoryForApp];
CGFloat batteryUsageValue = [[DynamicLevelMonitor sharedInstance] batteryUsageForApp];
[[DynamicLevelDecision sharedInstance] calculatePerformanceLevelWithMemoryValue:memoryUsageValue
cpuValue:cpuUsageValue
batteryValue:batteryUsageValue
completionBlock:^(MemoryUsageLevel memoryLevel, CPUUsageLevel cpuLevel, BatteryUsageLevel batteryLevel, MultiplePerformanceLevel performanceLevel) {
/// 判断级别是否发生变化,发送性能降级或恢复原有等级的通知
if (performanceLevel != self.currentPerformanceLevel) {
[self postPerformanceNotifiWithPerformanceLevel:performan