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可以帶來的網絡層效率提高其實也沒有文獻上的圖表那麼明顯,但仍是有性能提高的。還有另一種比較笨的連接複用的方法,就是維護一個隊列,而後將隊列裏的請求壓縮成一個請求發出去,之因此會存在滯留在隊列中的請求,是由於在上一個請求還在外面飄的時候。這種作法最終的效果表面上看跟連接複用差異不大,但並非真正的連接複用,只能說是請求合併。

相關文章
相關標籤/搜索