• 本站分享从数据采集到数据应用全链条知识,包含数据仓库搭建、数据分析、模型算法、数据平台系统、数据产品等。
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

有赞 App 如何实现动态域名

全部文章 hey 5年前 (2020-10-16) 551次浏览 0个评论 扫描二维码

一、概述

在移动开发中,网络层面的监控一直是非常有必要的,比如统计网络接口的失败率、重定向网络请求、网络 Request 增加公共 header 头、实现动态域名等等。经常会遇到 App 某些域名因为一些原因在某些地区 DNS 解析异常,因此我们需要将这些有问题的域名进行动态替换,让用户可以正常的访问接口,正常使用我们的 App。
二、具体方案
动态域名其实就是网络请求的 URL 的 Host 实现动态替换的能力,我们可以从监听、拦截网络请求方面入手来达到动态域名替换的目的。有赞目前的 App 大都使用 Weex、Flutter、H5 进行跨平台开发,在技术选择上我们尽量做到统一,沉淀出一套通用能力。由于 Weex 网络请求采用原生桥接的方式,因此对于 Weex 和 Native 的网络请求,只需要对 Native 端网络请求做处理,最终采用拦截 Native 网络请求的方式,Flutter 和 H5 会在后文介绍。
2.1 配置中心结合 Native
有赞配置中心平台是为了满足 App 灵活开关配置类需求开发的统一管理平台,可以对差异的功能划分不同的组件,给运营人员和开发人员发布新配置的功能,结合长连接能力,能够达到实时获取配置效果。那我们的思路就是利用配置中心的能力,结合 Native 网络拦截方法实现 App动态域名能力,流程如下图所示:
有赞 App 如何实现动态域名整个方案存在一个问题,我们可以设想一下,万一配置中心的域名 DNS 解析异常,我们该如何去做?
2.2 配置中心备用域名
我们从运维那边申请了配置中心的备用域名,在配置中心请求接口做了降级策略,这样就可以保证配置中心接口在极端情况下也可以正常访问,整体方案流程如下图所示:
有赞 App 如何实现动态域名
三、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 架构图:
有赞 App 如何实现动态域名在每一个 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 已经处理过,那么我们就不再进行处理。

有赞 App 如何实现动态域名

我们在 + canInitWithRequest: 中判断是否有处理过的标志,来进行拦截。

有赞 App 如何实现动态域名 3.1.5 开始网络请求

当我们处理了这个请求后,就需要我们手动去发送这个网络请求,即重写 starLoading 方法。

有赞 App 如何实现动态域名

这里我简化了代码,在这个方法里面根据配置中心下发的 replaceHost 域名可以对 targetHost 域名进行动态替换,也可以将 request 做一些自定的处理,比如增加统一的 header 头等处理。

3.1.6 停止相应的请求

有赞 App 如何实现动态域名

取消网络请求的 task,将 task 置为 nil。
3.1.7 实现 NSURLSessionTaskDelegate

有赞 App 如何实现动态域名

然后将自定义的 protocol 注册到 NSURLProtocol 中即可这样就可以拦截 UIWebView 和自定义的网络请求了,如果要拦截 AFNetworking、Alamofire 等三方库请求,我们需要将 NSURLSessionConfiguration 类,用 Method Swizzle 将 protocolClasses 替换成自己定义的 CustomeURLProtocol。以上就是自定义 NSURLProtocol 大体流程,配合上配置中心,我们就可以实现动态域名替换,当然你还可以做以下事情:

3.2 Android

3.2.1 对 OKHttp 插桩

Android 端 App 基本使用 OKHttp 和 HttpUrlConnection/HttpsUrlConnection 两种框架来进行网络请求,因此只需要对以上两种网络请求做插桩来达到网络请求拦截的效果。方案图如下:
有赞 App 如何实现动态域名
3.2.2 插桩实现

有赞 App 如何实现动态域名

有赞 App 如何实现动态域名

拿到 OkHttpClient 之后可以设置很多属性如:

有赞 App 如何实现动态域名  3.2.3 UrlConnection 插桩

通过以下方式插桩可以拿到 URLConnection 的入参 URL,因此也可以动态控制域名。

有赞 App 如何实现动态域名

有赞 App 如何实现动态域名

3.2.4 使用
a、build.gradle 添加引用有赞 App 如何实现动态域名 b、app/build.gradle 添加代码扫描配置有赞 App 如何实现动态域名 c、Application 中主动拉取动态域名配置有赞 App 如何实现动态域名 d、扩展能力有赞 App 如何实现动态域名
四、跨平台

4.1 Flutter

目前我们使用的 Flutter 网络请求分为:图片下载请求和普通数据网络请求,数据网络请求我们采用插件方式,封装了 Native 的网络请求库,不需要做单独的处理,图片加载使用的 Flutter 自己的渲染引擎,下面来介绍下 Flutter 图片下载如何去做动态域名。

4.1.1 Flutter 渲染流程图
有赞 App 如何实现动态域名Layer Tree:这个是 dart runtime 输出的一个树状数据结构,树上的每一个叶子节点,代表了一个界面元素(Button,Image 等等)。
Skia:这个是谷歌的一个跨平台渲染框架,从目前 iOS 和 Anrdroid 来看,SKIA 底层最终都是调用 OpenGL 绘制。Vulkan 支持还不太好,Metal 还不支持。
Shell:这里的 Shell 特指平台特性(Platform)的那一部分,包含 IOS 和 Android 平台相关的实现,包括 EAGLContext 管理、上屏的操作以及后面将会重点介绍的外接纹理实现等等。
从图中可以看出,当 Runtime 完成 Layout 输出一个 Layertree 以后,在管线中会遍历 Layertree 的每一个叶子节点,每一个叶子节点最终会调用 Skia 引擎完成界面元素的绘制,在遍历完成后,在调用 glPresentRenderBuffer(IOS)或者 glSwapBuffer(Android) 按完成上屏操作。
基于这个基本原理,Flutter 在 Nativ e 和 Flutter Engine 上实现了 UI 的隔离,书写 UI 代码时不用再关心平台实现从而实现了跨平台。
4.1.2 外接纹理
上图是前文提到的 LayerTree 的一个简单架构图,每一个叶子节点代表了 dart 代码排版的一个控件,可以看到最后有一个 TextureLayer 节点,这个节点对应的是 Flutter 里的 Texture 控件(这里的 Texture 和 GPU 的 Texture 不一样,这个是 Flutter 的控件)。当在 Flutter 里创建出一个 Texture 控件时,代表的是在这个控件上显示的数据,需要由 Native 提供。以 iOS 端为例,TexttureLayer 节点的最终绘制整体过程可以分为三步:调用 external_texture copyPixelBuffer,获取 CVPixelBuffer CVOpenGLESTextureCacheCreateTextureFromImage 创建 OpenGL 的 Texture 将 OpenGL Texture 封装成 S KImage,调用 Skia 的 DrawImage 完成绘制。
通过外接纹理的方式,实际上 Flutter 和 Native 传输的数据载体就是 PixelBuffer,Native 端的数据源将数据写入 PixelBuffer,Flutter 拿到 PixelBuffer 以后转成 OpenGLES Texture,交由 Skia 绘制。
4.1.3 ShareGroup
App 中使用 OpenGL 来渲染都会有两个线程,一个负责加载资源,一个负责渲染的方式。这两个线程会共用一个 EAGLContext。Flutter 在 EAGLContext 的处理上采用两个线程彼此通过 ShareGroup 来共享纹理数据。在 Flutter 创建的 Context 时,将它们的 ShareGroup 透出。在 Native 通过 OpenGL 渲染的模块创建 Context 时,在 Native 侧保存好这个 ShareGroup ,这样当 Native 创建 Context 时,都会使用这个 ShareGroup 进行创建,这样就实现了 Native 和 Flutter 之间的纹理共享。
有赞 App 如何实现动态域名
4.1.4 原生处理
总结整个方案,通过外接纹理的方式,Flutter 就可以很容易绘制出大型图片加载库 SDWebImage 等,本质是共用了一套缓存,将图片网络加载的工作转移到了 Native 端,从而实现了图片 URL动态域名的需求,至于网络请求,Flutter 完全可以使用网络库插件,本质也是调用 Native 的网络库。
4.2 H5
上面介绍的 Native 方法对于 H5 请求来说并不能做到拦截网络,比如 iOS 基于 NSURLProtocol 实现自定义拦截网络请求,并不能拦截 WKWebView 的网络请求,市面上也有很多方法可以拦截 WKWebVIew 网络请求。我们这边的方案是让前端来对域名进行动态配置,如果检测到域名访问异常,就激活配置中心,替换新的域名让商家能够正常的访问,整体的业务流程设计如下图所示:
有赞 App 如何实现动态域名
五、总结与展望
未来将拦截网络请求的效果达到最大化,可以监控网络 Request 和 Response;可以做到统计接口失败率;可以做到 App 内部统计一些接口访问量;App 内所有特定请求增加公共的 header;可以返回自定义的 Response 等等,简单来讲就是网络数据的收发,都可以监控并做自定义操作。
本文章讲述了 Native、Flutter、H5 端实现动态域名的技术方案。iOS 端采用继承 NSURLProtocol 来实现对网络拦截、Android 端采用插桩来达到网络请求拦截,最终都配合配置中心动态下发域名来达到动态域名的目的。Flutter 端则采用外接纹理的方式,Native 和 Flutter 通过 PixelBuffer 作为载体来达到共用缓存的目的,通过图片加载插件,将下载图片的操作桥接到 Native 端,最终也可以实现动态域名的目的。H5 则采用类似配置中心的下发配置统一收口网络请求策略来达到动态域名的目的,这三种方案结合依赖可以覆盖有赞App 的所有网络请求场景。以上是有赞 App 如何实现动态域名的策略介绍,欢迎各位一起讨论。
‍‍

版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:有赞 App 如何实现动态域名
喜欢 (0)

您必须 登录 才能发表评论!