IOS架构--网络层架构的设计方案

   网络层在架构设计中扮演着重要的角色,之前ASIHttpRequest比较红,如今的AFNetWorking大有霸屏的节奏,而MKNetworkKit貌似也还ok,不过也临近被抛弃的边缘。在这网红掌控的年代,咱们天然是谁最火就拥抱谁了。git

   那么想要设计出牛逼的网络层架构,咱们须要考虑些啥呢?哈哈,鄙人不才,说出几点供你们抛砖引玉,嘿嘿,好害羞!github

   本帅哥先从大体的方向来讲,主要是网络接口的设计,网络层的设计,安全机制以及性能的优化几个方向来着手。json

   网络接口的设计swift

  两层三部分数据结构数组

 

   接口返回数据第一次为字典,分为两层三部分:code、msg、data缓存

 

 "code": 0,
 "msg": "",
 "data": {
    "upload_log": true,
    "has_update": false,
    "admin_id": "529ecfd64"
}

 

  • code:错误码,能够记录下来快速定位接口错误缘由,能够定义一套错误码,好比200正常,1从新登陆...安全

  • msg:接口文案提示,包括错误提示,用来直接显示给用户,因此这一套错误提示就不能是什么一串英文错误了服务器

  • data:须要返回的数据,能够是字典,能够是数组网络

 

接口帮咱们定义了code和msg,是否是咱们就不须要作错误处理了?固然不是,服务端的错误逻辑毕竟是简单的,具体到data里面的数据处理可能还有错误,因此错误的处理是必不可少的,下面会单独对错误处理作介绍数据结构

 

   网络请求参数上传方式统一

 

这里通常都能作到,也有额外的,好比咱们的一个服务器接口作的比较早,当时POST接口使用的就不规范,普通的应用信息channelID、device_id使用的是拼接在字符串后面的方式,而真正的请求参数则须要转成json放在一个字段里面传递,就是接口GET、POST并存的方式,形成网络层须要作特殊处理

 

因此说标准的GET、POST请求方式是颇有必要的

 

  关于Null类型

 

你们都知道Null类型在iOS里面是很特殊的,个人建议是放在客户端来作,缘由有不少:

 

1)接口的规范定义并非每一个公司都是从一开始就能定义好的,老接口若是要把Null字段去掉的改动很是大

 

2)客户端用过一个接口过滤也能够解决,一劳永逸,不用再担忧由于某天接口的问题出现崩溃,并且经过一些Model的第三方库也能够很好的解决这个问题。这里不得说下swift的类型检测真是太方便了,以前一个项目用swift写的,代码规范一点,根本不会出现由于参数类型问题引发崩溃

 

 

多服务器多环境设置

标准的APP是有4个环境的,开发、测试、预发、正式,特别是服务器的代码,不能说全部的代码更改都在正式环境下,应该从开发->测试->预发->正式作代码的更新,开发就是新需求和优化的时候的更改,测试就是提交给测试人员后的更改,这个时候更改是在一个新的分支上,完成后要和合并到测试分支上并合并到开发分支上,预发这时候的变更就比较小了,通常会在测试人员完成后发布给全公司的人来测试,有问题了才会更改,更改后一样合并到开发分支,正式则是线上发布版本的紧急BUG修复,修改完后一样合并到开发分支上。因此开发分支是一直都是最新的。在此基础上可能会有其余的环境,好比hotfix环境,自定义的h5/后台本地调试的环境。

 

 

 

   网络层设计的问题

  1. 使用哪一种交互模式来跟业务层作对接?

  2. 是否有必要将API返回的数据封装成对象而后再交付给业务层?

  3. 使用集约化调用方式仍是离散型调用方式去调用API?

   iOS开发领域有不少对象间数据的传递方式,我看到的大多数App在网络层所采用的方案主要集中于这三种:Delegate,Notification,Block。KVO和Target-Action我目前尚未看到有使用的。我建议能够考虑使用block,由于代码能够更集中,而后使用起来更方便快捷。

  API返回的数据能够直接为数组或者字典的格式,中间封装一个业务处理器,而后交付给view。

  建议使用集约型的方式来调用API,这样更好地统一调用的方式。

     那咱们网络接口这边的设计原则是:

          尽量减小跨层数据交流的可能,限制耦合

          统一回调方法,便于调试和维护

          在跟业务层对接的部分只采用一种对接手段(在我这儿就是只采用delegate这一个手段)限制灵活性,以此来交换应用的可维护性

     

网络层的安全机制

判断API的调用请求是来自于通过受权的APP

  1. 使用这个机制的目的主要有两点:

  2. 确保API的调用者是来自你本身的APP,防止竞争对手爬你的API

   

保证传输数据的安全

使用这个机制的主要目的有两点:

  1. 防止中间人攻击,好比说运营商很喜欢往用户的Http请求里面塞广告...

  2. SPDY依赖于HTTPS,并且是将来HTTP/2的基础,他们可以提升你APP在网络层总体的性能。

解决方案:HTTPS

目前使用HTTPS的主要目的在于防止运营商往你的Response Data里面加广告啥的(中间人攻击),面对的威胁范围更广。从2011年开始,国外业界就已经提倡全部的请求(不光是API,还有网站)都走HTTPS,国内差很少晚了两年(2013年左右)才开始提倡这事,天猫是这两个月才开始作HTTPS的全APP迁移。

关于速度,HTTPS确定是比HTTP慢的,毕竟多了一次握手,但挂上SPDY以后,有了连接复用,这方面的性能就有了较大提高。这里的性能提高并非说一个请求原来要500ms能完成,而后如今只要300ms,这是不对的。所谓总体性能是基于大量请求去讨论的:一样的请求量(假设100个)在短时间发生时,挂上SPDY以后完成这些任务所要花的时间比不用SPDY要少。SPDY还有Header压缩的功能,不过由于一个API请求自己已经比较小了,压缩数据量所带来的性能提高不会特别明显,因此就单个请求来看,性能的提高是比较小的。不过这是下一节要讨论的事儿了,这儿只是顺带说一下。

安全机制小总结

这一节说了两种安全机制,通常来讲第一种是标配,第二种属于可选配置。不过随着我国互联网基础设施的完善,移动设备性能的提升,以及优化技术的提升,第二种配置的缺点(速度慢)正在愈来愈微不足道,所以HTTPS也会成为不久以后的将来App的网络层安全机制标配。各位架构师们,若是你的App尚未挂HTTPS,如今就已经能够开始着手这件事情了。

网络层的优化方案

网络层的优化手段主要从如下三方面考虑:

  1. 针对连接创建环节的优化

  2. 针对连接传输数据量的优化

  3. 针对连接复用的优化

这三方面是全部优化手段的内容,各类五花八门的优化手段基本上都不会逃脱这三方面,下面我就会分别针对这三方面讲一下各自对应的优化手段。

1. 针对连接创建环节的优化

在API发起请求创建连接的环节,大体会分这些步骤:

  1. 发起请求

  2. DNS域名解析获得IP

  3. 根据IP进行三次握手(HTTPS四次握手),连接创建成功

其实第三步的优化手段跟第二步的优化手段是一致的,我会在讲第二步的时候一块儿讲掉。

1.1 针对发起请求的优化手段

其实要解决的问题就是网络层该不应为此API调用发起请求。

  • 1.1.1 使用缓存手段减小请求的发起次数

对于大部分API调用请求来讲,有些API请求所带来的数据的时效性是比较长的,好比商品详情,好比App皮肤等。那么咱们就能够针对这些数据作本地缓存,这样下次请求这些数据的时候就能够没必要再发起新的请求。

通常是把API名字和参数拼成一个字符串而后取MD5做为key,存储对应返回的数据。这样下次有一样请求的时候就能够直接读取这里面的数据。关于这里有一个缓存策略的问题须要讨论:何时清理缓存?要么就是根据超时时间限制进行清理,要么就是根据缓存数据大小进行清理。这个策略的选择要根据具体App的操做日志来决定。

好比安居客App,日志数据记录显示用户平均使用时长不到3分钟,可是用户查看房源详情的次数比较多,而房源详情数据量较大。那么这个时候,就适合根据使用时长来作缓存,我当时给安居客设置的缓存超时时间就是3分钟,这样可以保证这个缓存可以在大部分用户使用时间产生做用。嗯,极端状况下作什么缓存手段不考虑,只要可以服务好80%的用户就能够了,并且针对极端状况采用的优化手段对大部分普通用户而言是没必要要的,作了反而会对他们有影响。

再好比网络图片缓存,数据量基本上都特别大,这种就比较适合针对缓存大小来清理缓存的策略。

另外,以前的缓存的前提都是基于内存的。咱们也能够把须要清理的缓存存储在硬盘上(APP的本地存储,我就先用硬盘来表示了,虽然不多有手机硬盘的说法,哈哈),好比前面提到的图片缓存,由于图片颇有可能在很长时间以后,再被显示的,那么本来须要被清理的图片缓存,咱们就能够考虑存到硬盘上去。当下次再有显示网络图片的需求的时候,咱们能够先从内存中找,内存找不到那就从硬盘上找,这都找不到,那就发起请求吧。

固然,有些时效性很是短的API数据,就不能使用这个方法了,好比用户的资金数据,那就须要每次都调用了。

  • 1.1.2 使用策略来减小请求的发起次数

这个我在前面提到过,就是针对重复请求的发起和取消,是有对应的请求策略的。咱们先说取消策略。

若是是界面刷新请求这种,并且存在重复请求的状况(下拉刷新时,在请求着陆以前用户不断执行下拉操做),那么这个时候,后面重复操做致使的API请求就能够没必要发送了。

若是是条件筛选这种,那就取消前面已经发送的请求。虽然颇有可能这个请求已经被执行了,那么取消所带来的性能提高就基本没有了。但若是这个请求还在队列中待执行的话,那么对应的此次连接就能够省掉了。

以上是一种,另一种状况就是请求策略:相似用户操做日志的请求策略。

用户操做会触发操做日志上报Server,这种请求特别频繁,可是是暗地里进行的,不须要用户对此有所感知。因此也不必操做一次就发起一次的请求。在这里就能够采用这样的策略:在本地记录用户的操做记录,当记录满30条的时候发起一次请求将操做记录上传到服务器。而后每次App启动的时候,上传一次上次遗留下来没上传的操做记录。这样可以有效下降用户设备的耗电量,同时提高网络层的性能。

小总结

针对创建链接这部分的优化就是这样的原则:能不发请求的就尽可能不发请求,必需要发请求时,能合并请求的就尽可能合并请求。然而,任何优化手段都是有前提的,并且也不能保证对全部需求都能起做用,有些API请求就是不符合这些优化手段前提的,那就老老实实发请求吧。不过这类API请求所占比例通常不大,大部分的请求都或多或少符合优化条件,因此针对发送请求的优化手段仍是值得作的。

1.2 & 1.3 针对DNS域名解析作的优化,以及创建连接的优化

其实在整个DNS链路上也是有DNS缓存的,理论上也是可以提升速度的。这个链路上的DNS缓存在PC用户上效果明显,由于PC用户的DNS链路相对稳定,信号源不会变来变去。可是在移动设备的用户这边,链路上的DNS缓存所带来的性能提高就不太明显了。由于移动设备的实际使用场景比较复杂,网络信号源会常常变换,信号源每变换一次,对应的DNS解析链路就会变换一次,那么原链路上的DNS缓存就不起做用了。并且信号源变换的状况特别特别频繁,因此对于移动设备用户来讲,链路的DNS缓存咱们基本上能够默认为没有。因此大部分时间是手机系统自带的本地DNS缓存在起做用,可是通常来讲,移动设备上网的需求也特别频繁,专门为咱们这个App所作的DNS缓存颇有可能会被别的DNS缓存给挤出去被清理掉,这种状况是特别多的,用户看一下子知乎刷一下微博查一下地图逛一逛点评再聊个Q,回来以后颇有可能属于你本身的App的本地DNS缓存就没了。这还没完,这里还有一个只有在中国特点社会主义的互联网环境中才会有的问题:国内的互联网环境因为GFW的存在,就使得DNS服务速度会比正常状况慢很多。

基于以上三个缘由所致使的最终结果就是,API请求在DNS解析阶段的耗时会不少。

那么针对这个的优化方案就是,索性直接走IP请求,那不就绕过DNS服务的耗时了嘛。

另一个,就是上面提到的创建连接时候的第三步,国内的网络环境分北网通南电信(固然实际状况更复杂,这里随便说说),不一样服务商之间的链接,延时是很大的,咱们须要想办法让用户在最适合他的IP上给他提供服务,那么就针对咱们绕过DNS服务的手段有一个额外要求:尽量不要让用户使用对他来讲很慢的IP。

因此综上所述,方案就应该是这样:本地有一份IP列表,这些IP是全部提供API的服务器的IP,每次应用启动的时候,针对这个列表里的全部IP取ping延时时间,而后取延时时间最小的那个IP做为从此发起请求的IP地址。

针对创建链接的优化手段实际上是跟DNS域名解析的优化手段是同样的。不过这须要你的服务器提供服务的网络状况要多,通常如今的服务器都是双网卡,电信和网通。因为中国特点的互联网ISP分布,南北网络之间存在瓶颈,而咱们App针对连接的优化手段主要就是着手于如何减轻这个瓶颈对App产生的影响,因此须要维护一个IP列表,这样就能就近链接了,就起到了优化的效果。

咱们通常都是在应用启动的时候得到本地列表中全部IP的ping值,而后经过NSURLProtocol的手段将URL中的HOST修改成咱们找到的最快的IP。另外,这个本地IP列表也会须要经过一个API来维护,通常是天天第一次启动的时候读一次API,而后更新到本地。

若是你还不熟悉NSURLProtocol应该怎么玩,看完官方文档这篇文章以及这个Demo以后,你确定就会了,其实很简单的。另外,刚才提到那篇文章的做者(mattt)还写了这个基于NSURLProtocol的工具,至关好用,是能够直接拿来集成到项目中的。

不用NSURLProtocol的话,用其余手段也能够作到这一点,但那些手段未免又比较愚蠢。

2. 针对连接传输数据量的优化

这个很好理解,传输的数据少了,那么天然速度就上去了。这里没什么花样能够讲的,就是压缩呗。各类压缩。

3. 针对连接复用的优化

创建连接自己是属于比较消耗资源的操做,耗电耗时。SPDY自带连接复用以及数据压缩的功能,因此服务端支持SPDY的时候,App直接挂SPDY就能够了。若是服务端不支持SPDY,也可使用PipeLine,苹果原生自带这个功能。

通常来讲业界内广泛的认识是SPDY优于PipeLine,而后即使如此,SPDY可以带来的网络层效率提高其实也没有文献上的图表那么明显,但仍是有性能提高的。还有另一种比较笨的连接复用的方法,就是维护一个队列,而后将队列里的请求压缩成一个请求发出去,之因此会存在滞留在队列中的请求,是由于在上一个请求还在外面飘的时候。这种作法最终的效果表面上看跟连接复用差异不大,但并非真正的连接复用,只能说是请求合并。

相关文章
相关标签/搜索