共计 9133 个字符,预计需要花费 23 分钟才能阅读完成。
前言
大数据分析最外围的是数据,咱们不仅仅要做到采集数据,还须要把数据上传到指定的服务端。而后再通过服务端的存储、抽取、剖析和展示,能力充分发挥数据真正的价值。
神策剖析 iOS SDK 针对数据传输,从完整性、正确性以及高效性等多方面综合思考,设计并实现了一套实用于数据采集的网络传输计划。
上面针对神策剖析 iOS SDK 网络模块进行解析,心愿可能给大家提供一些参考。
网络申请计划
iOS 中网络申请的实现有多种形式,例如:苹果官网提供的网络申请 API;或者一些开源网络框架。上面别离介绍这两种计划的优缺点。
苹果官网 API
基于苹果官网 API 实现的网络申请个别应用 NSURLSession 或者 NSURLConnection。然而,NSURLConnection 发送网络申请的相干接口在 iOS 9.0 开始已标记为过期。因而,开发者根本都退出了 NSURLSession 的营垒。上面咱们看下 NSURLSession 有哪些优缺点。
基于 NSURLSession 实现网络申请计划具备以下长处:
基于连贯的认证计划;
反对 HTTP 申请配置;
可私有化存储对象;
反对后盾上传下载。
然而,也存在以下毛病:
没有一些开源网络框架的性能易用,例如:构建简单的网络申请和响应的解决;
局部细节没有封装,须要本人解决。
开源网络框架
基于开源网络框架实现网络申请计划具备以下长处:
支流的开源网络框架功能丰富、绝对稳固。例如:AFNetworking 等;
能够方便快捷地实现网络申请的性能。
然而,也存在以下毛病:
性能较多,代码逻辑简单,学习老本较高;
外部缺点修复难度大甚至须要依赖作者来更新保护;
蕴含很多可能应用不到的性能;
引入开源网络框架后导致体积增大。
基于开源网络框架实现网络申请计划有利有弊,大家能够依据理论需要抉择适合的计划。
SDK 网络模块
如果 SDK 网络模块基于开源网络框架实现,客户在集成 SDK 的同时也应用了同样的开源网络框架就会导致抵触。另外,保护开源网络框架会存在版本更新不及时、问题排查艰难等危险。
因为基于开源网络框架存在这些弊病,SDK 网络模块采纳 NSURLSession 的形式实现。
NSURLSession 是零碎提供的网络拜访 API,不仅能够满足 SDK 网络申请的须要,而且性能稳固、易拓展。
实现原理
NSURLSession 能够创立一个或者多个实例,每个实例协调一组相干的数据传输工作。例如:在创立一个 Web 浏览器时,App 可能会为每个选项卡或者窗口创立一个会话。一个会话用于用户交互应用,另一个会话用于后盾下载。在每个会话中,App 增加了一系列工作,每个工作都是示意对特定 URL 的申请。
具体实现
网络相干配置
SDK 能够对数据发送进行一系列的配置,开发者能够依据具体需要设置相应的配置,从而达到高效的数据发送成果。
SDK 的相干配置在初始化时实现,能够配置的参数如下:
serverURL:数据发送地址,采集的数据会发送到该地址;
flushInterval:两次数据发送的最小工夫距离(单位毫秒),默认为 15000 毫秒;
flushBulkSize:两次数据发送的最小缓存条数,当本地缓存条数达到 flushBulkSize 时则会发送数据,默认为 100 条;
securityPolicy:SSL 证书和自签名证书的配置;
flushNetworkPolicy:数据发送时的网络策略。
数据发送线程封装
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]; }];}
数据发送策略
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();});}
除了下面提到的发送策略外,还会在触发以下事件时发送本地数据库中的数据:
触发激活事件;
触发 $SignUp 事件;
触发 $AppRemoteConfigChanged 事件;
通过近程配置禁用 SDK 时,会发送本地数据库中的全副数据。
数据安全
数据加密
SDK 发送的数据波及到用户隐衷,爱护用户隐衷是开发者应尽的责任和任务。SDK 提供数据加密性能,避免数据在传输过程中产生泄露。
目前 SDK 应用混合加密策略(RSA + AES)对数据进行加密,具体可分为两局部:
应用生成的 AES 密钥对采集的数据进行 AES 对称加密;
应用 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 的密钥长度会更小,加密速度也更快。
因而,SDK 提供的另外一种混合加密策略就是 ECC + AES,具体也能够分为两局部:
应用生成的 AES 密钥对采集的数据进行 AES 对称加密;
应用 ECC 公钥对 AES 密钥进行加密。
目前此性能还在开发中,置信很快就能够投入使用。
自签证书
SDK 中提供了 SASecurityPolicy 类(参考 AFNetworking 中的 AFSecurityPolicy 类实现,应用办法也相似),次要是验证 HTTPS 证书是否正确。
上面咱们看下证书校验的策略:
SASSLPinningModeNone:示意客户端无条件地信赖服务端返回的证书;
SASSLPinningModeCertificate:示意客户端会将服务端返回的证书与本地保留证书中的“所有内容”进行校验;
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:[NSBundle mainBundle]];
留神:iOS 只反对 DER 格局的证书,其余格局证书能够应用 OpenSSL 进行格局转换。
数据发送流程
数据采集时,属性信息须要封装成神策须要的 json 格局,而后存储到数据库中。
当 SDK 触发一条数据时会检测以后是否处于 Debug 模式或者是否超过本地数据库最大缓存,如果达到上述条件会发送数据。
不满足时会进行如下判断:如果触发的事件为“$SignUp”、本地缓存的数据条数大于设置的 flushBulkSize、间隔上次发送工夫距离大于 flushBulkSize 等。满足任意一条都会发送数据,具体流程如图 3-1 所示:
图 3-1 数据发送流程图
数据发送时,以下状况不会发送数据:
serverURL 为空时不会发送数据;
无网络时不会发送数据;
不满足 SDK 设置的网络发送策略时不会发送数据。
满足发送条件时,SDK 会将本地的数据全副发送。如果一次性传输的数据较多,会减少发送失败的概率,同时对性能的影响也比拟大。因而,SDK 一次最多读取 50 条数据,对读取的原始数据先采纳 Gzip 压缩,而后对压缩的内容进行 Base64 编码,保障高效传输。
数据发送后,在本地标记该局部数据正在发送。而后依据网络申请状态码判断是否发送胜利:状态码在 200 ~ 300 之间时,SDK 认为发送数据是胜利的,会先标记本地发送的数据状态为胜利发送,而后删除本地数据数据。发送失败时,本地数据不会删除,并把数据标记为未发送。
每次发送都会循环读取本地存储的数据,直到数据全副发送实现。
数据发送校验
开发者在应用 SDK 的过程中,须要校验 SDK 采集的数据是否胜利地发送到指定的服务端。因而,SDK 提供了查看日志以及应用动静调试模式等形式来校验数据是否胜利发送。
控制台日志校验
在 Xcode 控制台查看数据是否胜利发送,须要在初始化时开启日志打印,SDK 在满足发送策略时会发送数据到指定的服务端。
控制台显示的日志具体分以下几种状况:
埋点事件触发胜利时,输入“track event”结尾的事件数据;
埋点事件触发失败时,输入相应的谬误起因;
事件数据发送胜利时,输入“valid message”结尾的事件数据;
事件数据发送失败时,输入“invalid message”结尾的事件数据并输入谬误起因。
开发过程中,能够依据日志判断数据是否胜利发送。
动静调试模式
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。具体的应用办法是:
先应用调试设施扫描网页二维码,开启该设施的“调试模式”;
点击开始刷新后,操作 App 触发事件;
事件上传胜利就会在 Debug 实时数据查看中看到对应的事件。
总结
本文次要介绍了神策剖析 iOS SDK 网络模块的具体实现。SDK 网络申请没有基于开源网络框架来实现,防止了一些潜在的危险。通过对系统类 NSURLSession 进行封装、采纳欠缺的发送策略、以及对数据进行压缩、编码、校验等操作,实现了数据及时、精确、高效地发送。
最初,心愿通过这篇文章,大家可能对神策剖析 iOS SDK 的网络模块有一个零碎的理解。
参考文献
https://developer.apple.com/v…
https://wiki.openssl.org/inde…
https://github.com/AFNetworki…
文章起源:公众号神策技术社区