一、概述
在移动开发中,网络层面的监控一直是非常有必要的,比如统计网络接口的失败率、重定向网络请求、网络Request增加公共header头、实现动态域名等等。经常会遇到App某些域名因为一些原因在某些地区DNS解析异常,因此我们需要将这些有问题的域名进行动态替换,让用户可以正常的访问接口,正常使用我们的App。
二、具体方案
动态域名其实就是网络请求的URL的Host实现动态替换的能力,我们可以从监听、拦截网络请求方面入手来达到动态域名替换的目的。有赞目前的App大都使用Weex、Flutter、H5进行跨平台开发,在技术选择上我们尽量做到统一,沉淀出一套通用能力。由于Weex网络请求采用原生桥接的方式,因此对于Weex和Native的网络请求,只需要对Native端网络请求做处理,最终采用拦截Native网络请求的方式,Flutter和H5会在后文介绍。
2.1 配置中心结合Native
有赞配置中心平台是为了满足App灵活开关配置类需求开发的统一管理平台,可以对差异的功能划分不同的组件,给运营人员和开发人员发布新配置的功能,结合长连接能力,能够达到实时获取配置效果。那我们的思路就是利用配置中心的能力,结合Native网络拦截方法实现App动态域名能力,流程如下图所示:
整个方案存在一个问题,我们可以设想一下,万一配置中心的域名DNS解析异常,我们该如何去做?
2.2 配置中心备用域名
我们从运维那边申请了配置中心的备用域名,在配置中心请求接口做了降级策略,这样就可以保证配置中心接口在极端情况下也可以正常访问,整体方案流程如下图所示:
Native技术方案
3.1 iOS
在iOS开发中.常用到的网络请求三方库有AFNetworking和Alamofire,它们的底层是基于苹果提供的NSURLConnection、NSURLSession网络库接口进行了封装,那么想要拦截到网络请求,就需要使用官方提供的处理URL数据的类NSURLProtocol 。
3.1.1 NSURLProtocol
An abstract class that handles the loading of protocol-specific URL data.苹果官方文档这样介绍NSURLProtocol,一个处理加载协议特定URL数据的抽象类,看起来像是一个协议,其实这是一个类,支持创建该子类来支持自定义网络请求,先看看URL Loading System架构图:
在每一个HTTP请求开始,URL会加载系统创建的NSURLProtocol对象处理对应的URL请求,根据文档我们只需要创建一个子类继承自NSURLProtocol,通过registerClass:方法注册我们自定义的网络协议类,来实现网络拦截的目的。那么,我们需要解决的问题就是使用自定义的NSURLProtocol来处理App所有的网络请求,苹果官方文档中CustomHTTPProtocol介绍了如何自定义NSURLPtotocol来实现网络拦截。回到之前的问题,我们如何使用NSURLProtocol拦截Http请求?只需要判断对于那些请求request需要处理;对于需要处理的request做出哪些处理;再将响应请求的数据传递给调用者。
这里我们将基于NSURLSession为例来说明如何进行自定义网络拦截,达到动态域名替换的目的。
3.1.2 返回需要控制的请求
在NSRULPtotocol,要知道哪些网络请求是需要被拦截,通过重写canInitWithRequest:比如我们可以拦截全部的http/https请求。
3.1.3 自定义request
每一次请求都会有一个NSURLRequest实例,上述方法会拿到所有的请求对象,我们就可以根据对应的请求选择是否处理该对象请求经过 + canInitWithRequest:方法过滤之后,我们得到了所有要处理的请求,接下来需要对请求进行一定的操作。这里对请求不做任何修改,直接返回。
3.1.4 对处理过的request进行标记
通过这两个方法,就已经能够拦截住iOS的网络请求了,但是我们需要对每个处理过的request进行标记,判断如果这个request已经处理过,那么我们就不再进行处理。
我们在 + canInitWithRequest: 中判断是否有处理过的标志,来进行拦截。
3.1.5 开始网络请求
当我们处理了这个请求后,就需要我们手动去发送这个网络请求,即重写starLoading方法。
这里我简化了代码,在这个方法里面根据配置中心下发的replaceHost域名可以对targetHost域名进行动态替换,也可以将request做一些自定的处理,比如增加统一的header头等处理。
3.1.6 停止相应的请求
取消网络请求的task,将task置为nil。
3.1.7 实现NSURLSessionTaskDelegate
然后将自定义的protocol注册到NSURLProtocol中即可这样就可以拦截UIWebView和自定义的网络请求了,如果要拦截AFNetworking、Alamofire等三方库请求,我们需要将NSURLSessionConfiguration类,用Method Swizzle将protocolClasses替换成自己定义的 CustomeURLProtocol。以上就是自定义NSURLProtocol大体流程,配合上配置中心,我们就可以实现动态域名替换,当然你还可以做以下事情:
统计网络接口的失败率
重定向网络请求
自定义修改request
自定义返回网络请求的结果