【ArchSummit】如何通过AIOps推动可量化的业务价值增长和效率提升?>>> 了解详情
写点什么

有赞零售 App 离线切换技术方案

  • 2019-08-18
  • 本文字数:3777 字

    阅读完需:约 12 分钟

有赞零售 App 离线切换技术方案

一、离线模式的价值

有赞零售客户端的用户是需要经营线下门店的商家,在商家的经营时间内,如果软件不能保证正常使用会导致经营效率下降,甚至客户流失。因此除了不断优化软件性能,降低崩溃率,还要做好异常情况的降级处理,比如遇到网络故障和服务器故障等情况时,软件要保证核心功能的可用性,此时软件的工作模式被称为离线模式。


在离线模式下,客户端不能和服务端进行正常的数据通信,所有的数据存储和计算逻辑都要客户端独立完成。目前有赞零售客户端在离线模式下支持登录、收银支付、订单管理、会员积分、部分营销活动等核心功能,即使在极端情况下,有赞零售客户端依然保证商户的经营活动正常进行。


在客户端离线解决方案中主要有两个问题:


1、如何准确及时的触发或退出离线模式。


2、离线场景下的各个业务如何进行数据处理和同步。


本文针对第一个问题,以 iOS 零售客户端为例对其技术实现细节展开分析。

二、离线切换的技术实现

首先我们要清楚商家在什么场景下需要切换到离线模式。试想一下这样的场景:


小明是一名门店收银员,此时正是店里的客流高峰期,收银机前等待结账的顾客已经排起了队,不凑巧的是,此时店里的网络信号很不稳定,开单会等待很久才能有结果,有时会提示网络超时,收银效率很低,排队等待的顾客开始有些不耐烦了,小明只能一边安抚顾客,一边尝试着进行开单收银操作。后来网络彻底断掉,软件无法进行开单操作了,排队的顾客放下准备买的东西离开了,小明只能干等着技术人员来修复网络。


如果以上场景,收银软件能够切换成离线模式,脱离对网络的依赖,确保现有的顾客都能顺利进行结账,收银效率和顾客体验都不会受到影响。此外也存在服务器出现故障的情况,导致客户端的数据请求失败,此时也需要切换到离线模式。因此有赞零售客户端设计了两种切换离线的功能:手动切换能力和针对断网和服务故障的自动切换能力。


为此我们设计一个离线模块用来实现离线模式的触发和退出,它位于业务层和网络层的中间。业务层中各业务模块通过依赖注入的方式获取离线的状态变化和原因,而离线场景下的具体功能由各业务模块实现。业务层通过网络层发送业务数据请求,如果返回的数据出现异常,网络模块会将错误分别发送给业务层和离线模块,离线模块分析接口信息和返回的数据,进而启动服务故障识别功能。



离线模块主要提供三个能力:


  • 离线状态管理

  • 网络故障检测

  • 核心服务故障检测

2.1 离线状态管理

是否处于离线状态是由三个因子共同决定的:


  • 标记离线

  • 网络故障

  • 服务故障


其中标记离线是用户想要主动启动离线模式时,点击指定按钮触发离线模式。网络故障和服务故障是触发离线状态的另外两个条件。我们通过有限状态机维护离线状态的变化,只有当标记离线、网络故障和服务故障的状态都是 False,才会恢复在线状态,否则一直是离线状态。


标记离线通过本地文件缓存来保存状态,因此软件崩溃并不会影响离线状态的管理。对于网络故障,细分为蜂窝移动网络故障还是 WIFI 故障。服务故障解析是根据报错的接口判断出是哪个业务域出的问题。网络故障和服务故障都是实时监测获取状态,具体的监测手段会在下文展开介绍。

2.2 网络故障检测


要实现网络状态的实时监测,我们首先考虑采用苹果官方提供的 Reachability 库,Reachability 的实现依赖于系统提供的 SCNetworkReachability 类,SCNetworkReachability 允许应用程序获取当前系统的网络配置情况,包括网络类型和当前的连接状态。


Reachability 的使用分为同步模式和异步模式。在同步模式下,应用程序主动调用 Reachability 的currentReachabilityStatus方法获取当前的网络连接状态。


currentReachabilityStatus{    NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL SCNetworkReachabilityRef");    NetworkStatus returnValue = NotReachable;    SCNetworkReachabilityFlags flags;    // 同步模式获取网络连接状态    if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))    {        returnValue = [self networkStatusForFlags: flags];    }
return returnValue;}
复制代码


在异步模式下,应用程序需要调用 Reachability 的startNotifier方法开启一个 RunLoop 去监听网络状态的变化,当网络状态发生变化时,Reachability 会执行ReachabilityCallback回调函数,在这个回调函数里会发送kReachabilityChangedNotification通知,应用程序监听这个通知就可以感知网络状态的变化。源码如下:


- (BOOL)startNotifier{    BOOL returnValue = NO;    SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};    // 构造一个监听网络连接状态的上下文信息,详细说明见下面
// 通过调用 SCNetworkReachabilitySetCallback 函数(并传入 Reachability 对象的 ref,以及根据 SCNetworkReachabilityCallBack 自定义的一个回调函数和上述 context)设置 ref 的网络连接状态变化时对应的回调函数为 ReachabilityCallback if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) { // 通过调用 SCNetworkReachabilityScheduleWithRunLoop 函数设置 Reachability 对象的 ref 在 Current Runloop 中对应的模式(kCFRunLoopDefaultMode)开始监听网络状态 if (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) { returnValue = YES; } }
// 如果监听成功,返回 YES,否则返回 NO return returnValue;}
...static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info){#pragma unused (target, flags) NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback"); NSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
Reachability* noteObject = (__bridge Reachability *)info; // 因为上述 context 传入的是 self(Reachability 对象),所以这里的 info 为 Reachability 对象类型
// 发送一个全局通知告诉监听者网络连接状态已发生改变,可通过 noteObject 获取状态 [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];}
复制代码


然而 Reachability 存在一个缺陷,当应用程序可以将一个数据包发出时,SCNetworkReachability 就认为网络是可达的,但是这个数据包是否到达目标地址,SCNetworkReachability 并不清楚,所以它并不能真正检测服务可达性。


于是我们采用一个三方开源库 RealReachability 的方案,这个库是在 Reachability 的基础上进行改进,借助系统的 Socket 库实现的 ping 功能,通过不断对目标地址发送 ping 请求,根据返回的结果判断目标地址是否可达,因而可以更准确的识别当前的网络状态。


在实际应用中,会遇到网络状况时好时坏的情况,RealReachability 的方案会造成业务层频繁地在离线模式和正常模式间来回切换,影响用户体验,有的场景下甚至会导致反复刷新页面,进而引起卡顿。为了解决这个问题,我们在 RealReachability 上再一次进行优化,加入网络防抖功能,它的机制是网络状态的变化不会实时影响离线状态,而是设置一个时间缓冲值 T1,当网络断开时,我们会等待 T1 时间再检查网络是否断开,如果此时网络已经恢复,不会触发离线模式,如果此时网络仍然是断开的,就触发离线模式。在这个缓冲时间内的网络反复变化不会影响离线状态,因此就不会造成频繁的离线切换。



此外 RealReachabilityping 检测的时间间隔为 T2,当发现网络连接断开时,为了及时地检测到网络的恢复,我们会以更快的频率进行 ping 操作,网络恢复后检测的时间间隔也会恢复到 T2。

2.3 服务故障检测

服务故障的检测流程如下图所示:



第一阶段是网络层错误分发,业务层通过网络层发送请求给相应的服务端,当返回的数据出现异常,网络层会启动错误处理的流程,一方面会把错误信息返回给业务层,另一方面把错误信息和请求接口信息一起发给离线模块。


第二阶段是错误信息的本地解析,离线模块维护一份核心业务白名单,这里会配置客户端使用的各个核心业务的接口信息,根据这个白名单可以判断请求的接口所属的业务领域,如果当前报错的接口命中了白名单,离线模块会认为核心业务存在故障风险。


第三阶段是服务端的故障检测,离线模块会请求 QoS 智能决策系统,它根据不同业务后端系统的报警信息来判断是否发生服务故障。如果它判断发生了服务故障,离线模块会收到通知,进而更新本地的离线状态。


第四阶段是故障恢复的检测,离线模块会启动后台线程定期去轮询 QoS 智能决策系统,直到它判断服务故障已经恢复,停止轮询并更新离线状态。

三、展望

本文介绍了零售客户端离线切换解决方案,接下来的问题是业务层如何进行数据处理和同步,以收银开单流程为例,涉及到账号、商品、营销、会员、支付、订单等多个业务模块,各个模块在离线状态下如何存储和处理数据,保证用户在离线状态下可以完成收银功能,以上这些问题我们会在后续文章里详细介绍,敬请期待。



本文转载自公众号有赞 coder(ID:youzan_coder)


原文链接


https://mp.weixin.qq.com/s/ZuQ85PnhOW94m0U4_Yk_2w


2019-08-18 08:003442

评论 1 条评论

发布
用户头像
看文章好像只写了判断离线,但是离线之后如何继续收银还没写完吧
2019-08-19 20:44
回复
没有更多了
发现更多内容

Java面试很难?啃完阿里老哥这套Java架构速成笔记,我都能拿30K

Java你猿哥

Java 架构 面试 分布式 java基础

大白话讲解synchronized锁升级套路

做梦都在改BUG

你真的理解 Golang 切片吗?全切片表达式及切片使用技巧

宇宙之一粟

Go 切片 三周年连更

我用Bitmap实现亿级数据统计

Java你猿哥

Java redis 架构 ssm BitMap

叹服!华为高工手写344页高性能Java架构核心原理实战大神手册

做梦都在改BUG

Java 架构 高并发

Alibaba内部首发“M8级”500页微服务架构手册,GitHub上杀疯了

Java你猿哥

Java 架构 微服务 ssm 架构师

聊聊智能语音交互系统

轻口味

AI 三周年连更

说一个大家都知道的 Spring Boot 小细节!

江南一点雨

Java springboot

一文理清 TiDB 与 MySQL 中的常用字符集及排序规则

TiDB 社区干货传送门

新版本/特性解读 6.x 实践

TiSpark v3.0.3 & v3.1.3 发布

TiDB 社区干货传送门

新版本/特性解读

阿里大佬纯手写Angular和Spring Boot全栈技能修炼手册

做梦都在改BUG

Java Spring Boot angular 全栈

boot-admin整合Quartz实现动态管理定时任务

Java你猿哥

Java 架构 ssm vue admin springboot整合quartz

TiDB x Flink x Iceberg 实时 ODS 实践

TiDB 社区干货传送门

vivo 推送系统的容灾建设与实践

vivo互联网技术

容灾 推送系统

Spring Boot扩展:深入分析 IoC 容器

Java你猿哥

Java spring ssm 架构师 spring ioc

IntelliJ IDEA太重量级了?不妨尝试一下新的IDE工具

Java你猿哥

Java emacs ssm IDEA IntelliJ IDEA

【4.21-4.28】写作社区优秀技术博文一览

InfoQ写作社区官方

热门活动 优质创作周报

TiDB x Flink 数据集成实践

TiDB 社区干货传送门

AI前沿速报0428:95%开发人员在用AI编程

AI探险家小新

人工智能 编程 程序员 新闻资讯 ChatGPT

政务智能办体验升级、乳腺癌创新药加速研发,飞桨和文心大模型驱动应用智能涌现

飞桨PaddlePaddle

百度飞桨

牛皮!GitHub上标星90.6K的Java面试指南+笔记

Java你猿哥

Java 程序员 面试 ssm java核心知识点

人类 vs AI:玩梗大作战,看看谁是最后的赢家?

LigaAI

测评 ChatGPT 文心一言 GPT-4 企业号 4 月 PK 榜

赋能产业新发展,华秋电子携多高层板积极参与成都工博会

华秋电子

手把手教你成为 TiDB 的 Contributor

TiDB 社区干货传送门

实践案例 故障排查/诊断 TiDB 源码解读

看完这篇推特视频怎么下载到手机相册的教程,我又可以了!

frank

twitter

汽车电阻:新能源时代来临,车用电阻市场机遇正在显现

华秋电子

软硬件负载均衡 IP 地址透传

TiDB 社区干货传送门

数据库架构设计 数据库连接

视频剪辑工具:Final Cut Pro 中文版

真大的脸盆

Mac 视频剪辑 Mac 软件 视频处理工具

华秋干货铺 | PCB阻抗计算的可制造性设计

华秋电子

太阳能是否能助力LED显示屏节能?

Dylan

节能 LED显示屏 太阳能

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结

JackJiang

网络编程 即时通讯 即时通讯IM

有赞零售 App 离线切换技术方案_技术管理_余颖_InfoQ精选文章