Mi jefe no permite AFNetworking, ¿qué debo hacer?

I. Introducción

El núcleo del análisis de big data son los datos. No solo necesitamos recopilar datos, sino también cargar los datos en el servidor designado. Luego, mediante el almacenamiento, la extracción, el análisis y la visualización del servidor, se puede utilizar por completo el valor real de los datos.

Para la transmisión de datos, Sensors Analytics iOS SDK ha diseñado e implementado un conjunto de soluciones de transmisión de red adecuadas para la recopilación de datos, considerando la integridad, corrección y eficiencia.

El siguiente es un análisis del módulo de red iOS SDK de Sensors Analysis, con la esperanza de brindarle alguna referencia.

En segundo lugar, el esquema de solicitud de red.

Hay muchas formas de implementar solicitudes de red en iOS, como: la API de solicitud de red oficial proporcionada por Apple o algunos marcos de trabajo de red de código abierto. Las ventajas y desventajas de estos dos esquemas se describen a continuación.

2.1 API oficial de Apple

Las solicitudes de red basadas en la API oficial de Apple generalmente usan NSURLSession o NSURLConnection. Sin embargo, la interfaz relacionada con NSURLConnection para enviar solicitudes de red se ha marcado como obsoleta desde iOS 9.0. Por lo tanto, los desarrolladores básicamente se han unido al campamento de NSURLSession. Echemos un vistazo a las ventajas y desventajas de NSURLSession.

Implementar un esquema de solicitud de red basado en NSURLSession tiene las siguientes ventajas [1]:

  1. esquema de autenticación basado en conexión;
  2. Admite configuración de solicitud HTTP;
  3. Objetos de almacenamiento privatizables;
  4. Admite carga y descarga en segundo plano.

Sin embargo, también existen las siguientes desventajas:

  1. No existe una función fácil de usar en algunos marcos de trabajo de red de código abierto, tales como: crear un procesamiento complejo de solicitudes y respuestas de red;
  2. Algunos detalles no están encapsulados y debe manejarlos usted mismo.

2.2 Marco de red de código abierto

Implementar un esquema de solicitud de red basado en un marco de red de código abierto tiene las siguientes ventajas:

  1. Los principales marcos de red de código abierto son ricos en funciones y relativamente estables. Por ejemplo: AFNetworking, etc.;
  2. La función de solicitud de red se puede realizar de forma cómoda y rápida.

Sin embargo, también existen las siguientes desventajas:

  1. Hay muchas funciones, la lógica del código es compleja y el costo de aprendizaje es alto;
  2. Es difícil reparar los defectos internos e incluso es necesario confiar en el autor para actualizar y mantener;
  3. Contiene muchas funciones que no se pueden usar;
  4. La introducción del marco de red de código abierto resultó en un aumento de tamaño.

Implementar un esquema de solicitud de red basado en un marco de red de código abierto tiene ventajas y desventajas. Puede elegir el esquema apropiado de acuerdo con sus necesidades reales.

3. Módulo de red SDK

如果 SDK 网络模块基于开源网络框架实现,客户在集成 SDK 的同时也使用了同样的开源网络框架就会导致冲突。另外,维护开源网络框架会存在版本更新不及时、问题排查困难等风险。

由于基于开源网络框架存在这些弊端,SDK 网络模块采用 NSURLSession 的方式实现。

NSURLSession 是系统提供的网络访问 API,不仅可以满足 SDK 网络请求的需要,而且功能稳定、易拓展。

3.1实现原理

NSURLSession 可以创建一个或者多个实例,每个实例协调一组相关的数据传输任务[1]。例如:在创建一个 Web 浏览器时,App 可能会为每个选项卡或者窗口创建一个会话。一个会话用于用户交互使用,另一个会话用于后台下载。在每个会话中,App 添加了一系列任务,每个任务都是表示对特定 URL 的请求。

3.2具体实现

3.2.1. 网络相关配置

SDK 可以对数据发送进行一系列的配置,开发者可以根据具体需求设置相应的配置,从而达到高效的数据发送效果。

SDK 的相关配置在初始化时完成,可以配置的参数如下:

  1. serverURL:数据发送地址,采集的数据会发送到该地址;
  2. flushInterval:两次数据发送的最小时间间隔(单位毫秒),默认为 15000 毫秒;
  3. flushBulkSize:两次数据发送的最小缓存条数,当本地缓存条数达到 flushBulkSize 时则会发送数据,默认为 100 条;
  4. securityPolicy:SSL 证书和自签名证书的配置;
  5. flushNetworkPolicy:数据发送时的网络策略。

3.2.2. 数据发送线程封装

SDK 数据发送是在子线程中完成的,当采集的数据满足发送策略时触发异步发送,上传任务会在 SAHTTPSession 类中完成。在初始化 SDK 时,创建 SAHTTPSession 实例,并实例化 NSOperationQueue 实现多线程发送事件。具体实现代码如下:


- (void)requestWithRecords:(NSArray<SAEventRecord *> *)records completion:(void (^)(BOOL success))completion {
    [SAHTTPSession.sharedInstance.delegateQueue addOperationWithBlock:^{
            ......
        // 网络请求回调处理
        SAURLSessionTaskCompletionHandler handler = ^(NSData * _Nullable data, NSHTTPURLResponse * _Nullable response, NSError * _Nullable error) {
            ......
        };
 
        // 转换成发送的 http 的 body
        NSData *HTTPBody = [self buildBodyWithJSONString:jsonString isEncrypted:isEncrypted];
        NSURLRequest *request = [self buildFlushRequestWithServerURL:self.serverURL HTTPBody:HTTPBody];
        NSURLSessionDataTask *task = [SAHTTPSession.sharedInstance dataTaskWithRequest:request completionHandler:handler];
        [task resume];
    }];
}
复制代码

3.2.3. 数据发送策略

SDK 在采集到数据后会先储存到本地的 SQLite 数据库,在满足下面的发送策略时才会发送:

  • 客户端本地存储数据超过一定条数(默认 100 条)

在 SDK 初始化时,可配置 flushBulkSize 来限制条数(默认为 100 条)。如果用户设置的条数小于 50 条,则为 50 条。SDK 采集的数据较多,如果设置发送策略中的限制条数太小会导致频繁的网络请求,从而影响性能。如果用户设置的限制条数太大,会导致一次发送的数据过多。这样不仅会导致上传时间延长,还可能导致上传失败的概率增大。

  • 两次数据发送间隔一定时间(默认 15 秒)

在 SDK 初始化时,可配置 flushInterval 来控制两次数据发送的时间间隔(默认 15 秒)。如果用户设置的时间间隔小于 5 秒,则为 5 秒。SDK 会开启一个定时器,每隔 15 秒发送数据。

  • App 退到后台发送数据

SDK 监听了 UIApplicationDidEnterBackgroundNotification 通知,在 App 退到后台时发送数据。具体代码如下:


- (void)applicationDidEnterBackground:(NSNotification *)notification {
    ......
    dispatch_async(self.serialQueue, ^{
        [self.eventTracker flushAllEventRecords];
        endBackgroundTask();
    });
}
复制代码

除了上面提到的发送策略外,还会在触发以下事件时发送本地数据库中的数据:

  1. 触发激活事件;
  2. 触发 $SignUp 事件;
  3. 触发 $AppRemoteConfigChanged 事件;
  4. 通过远程配置禁用 SDK 时,会发送本地数据库中的全部数据。

3.2.4. 数据安全

3.2.4.1. 数据加密

SDK 发送的数据涉及到用户隐私,保护用户隐私是开发者应尽的责任和义务。SDK 提供数据加密功能,防止数据在传输过程中发生泄露。

目前 SDK 使用混合加密策略(RSA + AES)对数据进行加密,具体可分为两部分:

  1. 使用生成的 AES 密钥对采集的数据进行 AES 对称加密;
  2. 使用 RSA 公钥对 AES 密钥进行加密。

加密后的数据格式如下:


[{
    "pkv": 1,
    "ekey": "e7lE4W67gUoER1al86Fg8CsMVhVpIDQReuONmwVyiIfZQA+U0J5J67UBnABFc6YKIYpWYPgAyQ5U+wPal17zOtyA7EeO2H+bxui1ESfKrh0pOaViElbHS7WTD9fBcAiOacNxukGlpjK70KWtSEFt+35XejWRw09AUIn8KeYSwnV7wetu4Ba783VvHsOd0vyWace3+I+T3tr7hiAnAxaeKtaYdeoKWCAydj8AM1jK+3z+kIc1aVTwDXKEw/Cw03EyO5wKF/0pHYBCkCnTRhXFIULVR6EDQWJh/fW0Bc5YpT2YDP9KRzXP6HfAML2/k7YkwuMRXhR4p12h0RPFxvmTRg==",
    "flush_time": 1542625604461,
    "payloads": ["Tg7E5sMghLePA3yW/1X6xO+MAPnncKvn9wYGk/T912JMljW0bK0hxXL14ttPY26uc1bksBHAqFW5xRb3LUYX+kcuM/N7shaw1/4XJcghw2JhexICA3Lf3Vsv37UtS0o8hW0LNq7kkSZt9wOa1Xb3agwtL7vhENtreqFBM+k+5ZH7MjVK8GalQdDauR7cZ1dtcprkFJiXuKrotp0DSeTCCtYiABlS3mDdVc1/NjbvvavbV4p4FC+R5VA8aDWszKC02gMyF4pohPKpYgJsFSwCKbNPoPY8TEop3HS6UnXDI6hlTPmzspaDfJHXThLhb83mnqWnrtQih2HllGUUthA2fKZj+QgqoGj7EHhjsGgaXxV0I7Op6NEe5EA1nOejOK8ibo7s0c67lkyb7AFcBfRNgJzhSkuZy3lqGXl61d1KIo3UT4+iBnRAgxZf", "Tg7E5sMghLePA3yW/1X6xO+MAPnncKvn9wYGk/T912JMljW0bK0hxXL14ttPY26uc1bksBHAqFW5xRb3LUYX+kcuM/N7shaw1/4XJcghw2JhexICA3Lf3Vsv37UtS0o8hW0LNq7kkSZt9wOa1Xb3agwtL7vhENtreqFBM+k+5ZH7MjVK8GalQdDauR7cZ1dtcprkFJiXuKrotp0DSeTCCtYiABlS3mDdVc1/NjbvvavbV4p4FC+R5VA8aDWszKC02gMyF4pohPKpYgJsFSwCKbNPoPY8TEop3HS6UnXDI6hlTPmzspaDfJHXThLhb83mnqWnrtQih2HllGUUthA2fKZj+QgqoGj7EHhjsGgaXxV0I7Op6NEe5EA1nOejOK8ibo7s0c67lkyb7AFcBfRNgJzhSkuZy3lqGXl61d1KIo3UT4+iBnRAgxZf", "Tg7E5sMghLePA3yW/1X6xO+MAPnncKvn9wYGk/T912JMljW0bK0hxXL14ttPY26uc1bksBHAqFW5xRb3LUYX+kcuM/N7shaw1/4XJcghw2JhexICA3Lf3Vsv37UtS0o8hW0LNq7kkSZt9wOa1Xb3agwtL7vhENtreqFBM+k+5ZH7MjVK8GalQdDauR7cZ1dtcprkFJiXuKrotp0DSeTCCtYiABlS3mDdVc1/NjbvvavbV4p4FC+R5VA8aDWszKC02gMyF4pohPKpYgJsFSwCKbNPoPY8TEop3HS6UnXDI6hlTPmzspaDfJHXThLhb83mnqWnrtQih2HllGUUthA2fKZj+QgqoGj7EHhjsGgaXxV0I7Op6NEe5EA1nOejOK8ibo7s0c67lkyb7AFcBfRNgJzhSkuZy3lqGXl61d1KIo3UT4+iBnRAgxZf"]
}, {
    "pkv": 1,
    "ekey": "e7lE4W67gUoER1al86Fg8CsMVhVpIDQReuONmwVyiIfZQA+U0J5J67UBnABFc6YKIYpWYPgAyQ5U+wPal17zOtyA7EeO2H+bxui1ESfKrh0pOaViElbHS7WTD9fBcAiOacNxukGlpjK70KWtSEFt+35XejWRw09AUIn8KeYSwnV7wetu4Ba783VvHsOd0vyWace3+I+T3tr7hiAnAxaeKtaYdeoKWCAydj8AM1jK+3z+kIc1aVTwDXKEw/Cw03EyO5wKF/0pHYBCkCnTRhXFIULVR6EDQWJh/fW0Bc5YpT2YDP9KRzXP6HfAML2/k7YkwuMRXhR4p12h0RPFxvmTRg==",
    "flush_time": 1542625604461,
    "payloads": ["Tg7E5sMghLePA3yW/1X6xO+MAPnncKvn9wYGk/T912JMljW0bK0hxXL14ttPY26uc1bksBHAqFW5xRb3LUYX+kcuM/N7shaw1/4XJcghw2JhexICA3Lf3Vsv37UtS0o8hW0LNq7kkSZt9wOa1Xb3agwtL7vhENtreqFBM+k+5ZH7MjVK8GalQdDauR7cZ1dtcprkFJiXuKrotp0DSeTCCtYiABlS3mDdVc1/NjbvvavbV4p4FC+R5VA8aDWszKC02gMyF4pohPKpYgJsFSwCKbNPoPY8TEop3HS6UnXDI6hlTPmzspaDfJHXThLhb83mnqWnrtQih2HllGUUthA2fKZj+QgqoGj7EHhjsGgaXxV0I7Op6NEe5EA1nOejOK8ibo7s0c67lkyb7AFcBfRNgJzhSkuZy3lqGXl61d1KIo3UT4+iBnRAgxZf", "Tg7E5sMghLePA3yW/1X6xO+MAPnncKvn9wYGk/T912JMljW0bK0hxXL14ttPY26uc1bksBHAqFW5xRb3LUYX+kcuM/N7shaw1/4XJcghw2JhexICA3Lf3Vsv37UtS0o8hW0LNq7kkSZt9wOa1Xb3agwtL7vhENtreqFBM+k+5ZH7MjVK8GalQdDauR7cZ1dtcprkFJiXuKrotp0DSeTCCtYiABlS3mDdVc1/NjbvvavbV4p4FC+R5VA8aDWszKC02gMyF4pohPKpYgJsFSwCKbNPoPY8TEop3HS6UnXDI6hlTPmzspaDfJHXThLhb83mnqWnrtQih2HllGUUthA2fKZj+QgqoGj7EHhjsGgaXxV0I7Op6NEe5EA1nOejOK8ibo7s0c67lkyb7AFcBfRNgJzhSkuZy3lqGXl61d1KIo3UT4+iBnRAgxZf", "Tg7E5sMghLePA3yW/1X6xO+MAPnncKvn9wYGk/T912JMljW0bK0hxXL14ttPY26uc1bksBHAqFW5xRb3LUYX+kcuM/N7shaw1/4XJcghw2JhexICA3Lf3Vsv37UtS0o8hW0LNq7kkSZt9wOa1Xb3agwtL7vhENtreqFBM+k+5ZH7MjVK8GalQdDauR7cZ1dtcprkFJiXuKrotp0DSeTCCtYiABlS3mDdVc1/NjbvvavbV4p4FC+R5VA8aDWszKC02gMyF4pohPKpYgJsFSwCKbNPoPY8TEop3HS6UnXDI6hlTPmzspaDfJHXThLhb83mnqWnrtQih2HllGUUthA2fKZj+QgqoGj7EHhjsGgaXxV0I7Op6NEe5EA1nOejOK8ibo7s0c67lkyb7AFcBfRNgJzhSkuZy3lqGXl61d1KIo3UT4+iBnRAgxZf"]
}]
复制代码

为了提高加密的效率和减少密钥的长度,经过调研以及大量的测试,最终选择了椭圆曲线加密算法(Elliptic Curve Cryptography,缩写:ECC)。

ECC 相较于 RSA 的优势是:在同等级别的安全性下,ECC 的密钥长度会更小,加密速度也更快[2]。

因此,SDK 提供的另外一种混合加密策略就是 ECC + AES,具体也可以分为两部分:

  1. 使用生成的 AES 密钥对采集的数据进行 AES 对称加密;
  2. 使用 ECC 公钥对 AES 密钥进行加密。

目前此功能还在开发中,相信很快就可以投入使用。

3.2.4.2. 自签证书

SDK 中提供了 SASecurityPolicy 类(参考 AFNetworking 中的 AFSecurityPolicy 类实现,使用方法也类似[3]),主要是验证 HTTPS 证书是否正确。

下面我们看下证书校验的策略:

  1. SASSLPinningModeNone:表示客户端无条件地信任服务端返回的证书;
  2. SASSLPinningModeCertificate:表示客户端会将服务端返回的证书与本地保存证书中的 “所有内容” 进行校验;
  3. SASSLPinningModePublicKey:表示客户端会将服务端返回的证书与本地保存证书中的 “PublicKey部分” 进行校验。

SDK 初始化后会默认创建一个 SASSLPinningModeNone 的 SASecurityPolicy 对象,可以使用如下代码支持自签证书:


SASecurityPolicy *securityPolicy = [SensorsAnalyticsSDK sharedInstance].securityPolicy;
  
/* allowInvalidCertificates 是否需要验证自签证书,默认为 NO
 */
securityPolicy.allowInvalidCertificates = YES;
  
/*
 validatesDomainName 是否需要验证域名,默认为 YES
 */
securityPolicy.validatesDomainName = NO;
  
/*
 pinnedCertificates 存储本地证书
 */
securityPolicy.pinnedCertificates = [SASecurityPolicy certificatesInBundle
复制代码

注意:iOS 只支持 DER 格式的证书,其他格式证书可以使用 OpenSSL 进行格式转换。

3.2.5. 数据发送流程

数据采集时,属性信息需要封装成神策需要的 json 格式,然后存储到数据库中。

当 SDK 触发一条数据时会检测当前是否处于 Debug 模式或者是否超过本地数据库最大缓存,如果达到上述条件会发送数据。

不满足时会进行如下判断:如果触发的事件为 “$SignUp” 、本地缓存的数据条数大于设置的 flushBulkSize 、距离上次发送时间间隔大于 flushBulkSize 等。满足任意一条都会发送数据,具体流程如图 3-1 所示:

image.png

图 3-1 数据发送流程图

数据发送时,以下情况不会发送数据:

  • serverURL 为空时不会发送数据;
  • 无网络时不会发送数据;
  • 不满足 SDK 设置的网络发送策略时不会发送数据。

满足发送条件时,SDK 会将本地的数据全部发送。如果一次性传输的数据较多,会增加发送失败的概率,同时对性能的影响也比较大。因此,SDK 一次最多读取 50 条数据,对读取的原始数据先采用 Gzip 压缩,然后对压缩的内容进行 Base64 编码,保证高效传输。

数据发送后,在本地标记该部分数据正在发送。然后根据网络请求状态码判断是否发送成功:状态码在 200 ~ 300 之间时,SDK 认为发送数据是成功的,会先标记本地发送的数据状态为成功发送,然后删除本地数据数据。发送失败时,本地数据不会删除,并把数据标记为未发送。

每次发送都会循环读取本地存储的数据,直到数据全部发送完成。

3.2.6. 数据发送校验

开发者在使用 SDK 的过程中,需要校验 SDK 采集的数据能否成功地发送到指定的服务端。因此,SDK 提供了查看日志以及使用动态调试模式等方式来校验数据是否成功发送。

3.2.6.1. 控制台日志校验

在 Xcode 控制台查看数据是否成功发送,需要在初始化时开启日志打印,SDK 在满足发送策略时会发送数据到指定的服务端。

控制台显示的日志具体分以下几种情况:

  • 埋点事件触发成功时,输出 “track event” 开头的事件数据;
  • 埋点事件触发失败时,输出相应的错误原因;
  • 事件数据发送成功时,输出 “valid message” 开头的事件数据;
  • 事件数据发送失败时,输出 “invalid message” 开头的事件数据并输出错误原因。

开发过程中,可以根据日志判断数据是否成功发送。

3.2.6.2. 动态调试模式

SDK 提供了动态调试发送数据的功能,方便开发者在使用 SDK 时校验数据。动态调试模式下 SDK 采集的数据会实时地发送到指定的服务端,同时提供了 DEBUG_ONLY 和 DEBUG_AND_TRACK 两种模式:

  • DEBUG_ONLY:实时发送数据但不会入库,避免在测试过程中产生的脏数据入库;
  • DEBUG_AND_TRACK:实时发送数据,同时也会入库。

那神策分析的服务端如何知道 SDK 开启了动态调试模式呢?答案是我们在发送请求时把 serverURL 中的 “sa” 替换成了 “debug”,这样服务端就会知道该数据是动态调试的数据。

那 DEBUG_ONLY 和 DEBUG_AND_TRACK 这两种模式,服务端又是如何区分的呢?答案是当我们在使用 DEBUG_ONLY 时会在请求头中添加属性 “Dry-Run”,用于区分 DEBUG_ONLY 和 DEBUG_AND_TRACK。

动态调试模式需要使用 Scheme,Scheme 正确配置后可以通过扫描 Debug 实时数据查看中的二维码拉起 App。具体的使用方法是:

  1. 先使用调试设备扫描网页二维码,开启该设备的 “调试模式”;
  2. 点击开始刷新后,操作 App 触发事件;
  3. 事件上传成功就会在 Debug 实时数据查看中看到对应的事件。

四、总结

本文主要介绍了神策分析 iOS SDK 网络模块的具体实现。SDK 网络请求没有基于开源网络框架来实现,避免了一些潜在的风险。通过对系统类 NSURLSession 进行封装、采用完善的发送策略、以及对数据进行压缩、编码、校验等操作,实现了数据及时、准确、高效地发送。

最后,希望通过这篇文章,大家能够对神策分析 iOS SDK 的网络模块有一个系统的了解。

参考文献:

[1]developer.apple.com/videos/play…

[2]wiki.openssl.org/index.php/E…

[3]github.com/AFNetworkin…

Supongo que te gusta

Origin juejin.im/post/6968405374143561765
Recomendado
Clasificación