1. 程式人生 > >App 後臺架構設計方案 設計思想與最佳實踐

App 後臺架構設計方案 設計思想與最佳實踐

做App做的久了,就想研究一下與之相關的App後臺,發現也是蠻有趣的。App後臺的兩個重要作用就是 遠端儲存資料 和 訊息中轉。這裡面的知識體系也是相當複雜,做好一個App後臺也是需要長期錘鍊的。本篇文章從 App 後臺架構 的角度介紹。好了,下面進入正題:

說起架構,我們先看一下何為架構,百度百科是這樣說的:架構,又名軟體架構,是有關軟體整體結構與元件的抽象描述,用於指導大型軟體系統各個方面的設計。那麼我們也可以看出,架構是和業務緊密相關的,是由業務驅動的。

由於App客戶端的特性,因此App後臺對技術實現和一般的Web後臺是有區別的。首先看一個適合App開發的開發模式:

1.敏捷開發模式

這裡推薦Scrum這個敏捷開發框架,具體可以檢視Scrum官網學習使用,這裡只是引入。

Scrum流程如下圖:

Scrum流程

2.選擇合適的資料庫產品和伺服器系統

資料庫產品眾多,這裡我就針對RedisMongoDBMySQL還有MySQL的分支MariaDB展開說明:

1.資料庫產品

資料庫 資料存放位置 查詢資料的區別
Redis 記憶體 基於鍵值對儲存,讀寫速度快
MongoDB 同時使用了硬碟和記憶體 每個資料有一個id(索引),知道id(索引)查詢速度快,不知道id(索引)效率低
MySQL(MongoDB) 硬碟 每個資料有一個id(索引),知道id(索引)查詢速度快,不知道id(索引)效率低

然後根據不同的產品需求選擇恰當的資料庫產品,如果沒有特殊的需求,Redis做快取系統,MySQL 或 MariaDB 做資料庫(常見的設定是 資料庫預設字符集utf8,預設排序utf8_general_ci) 將會是很好的選擇。

軟體優化:

1)正確使用MyISAM和InnoDB儲存引擎
2)正確使用索引
3)避免使用 select *
4)欄位儘可能的設定 非NULL

硬體優化:

1)增加實體記憶體
2)增加應用快取
3)使用SSD硬碟

架構優化:

1)分表
2)讀寫分離

讀寫分離架構

3)分庫(把一張表的資料分別儲存在不同的資料庫,可用MyCat實現,MyCat,關係型資料庫分散式處理軟體)。
MyCat以代理伺服器的形式位於App伺服器和後臺資料庫之間,
對外開放的介面是MySQL通訊協議,將App伺服器傳過來的sql語句按照路由的規則拆解轉發到不同的後臺資料庫,並把結果彙總返回。
MyCat部署模型如下:

MyCat部署模型

2.伺服器系統

CentOS 則是一個不錯的選擇。關於伺服器的部署,我在之前已經介紹過了,地址如下:

下面補充兩個常見的Linux命令:

top           顯示系統資源情況
netstat       檢視網路相關資訊

3.選擇合適的訊息佇列軟體

當後臺系統發現完成某些小任務需要花費很多時間,而且遲點晚成也不影響整個任務的完成進度時,就會把這些小任務交給訊息佇列。例如傳送郵件、簡訊、推送訊息等任務都非常適合在訊息佇列中處理。

把這些任務放在訊息佇列中,可加快App後臺請求都響應時間。同時訊息佇列也能把大量的併發請求變成序列的請求,來減輕伺服器的負擔。

常見的訊息佇列軟體有:

訊息佇列軟體 說明
RabbitMQ 重量級,適合企業級的開發,自帶Web監控介面,方便監控佇列的情況
Redis 輕量級,是一個key-value系統,但是也支援訊息佇列這種資料結構,App後臺中Redis被廣泛使用
ZeroMQ 號稱最快,尤其針對大吞吐量的需求場景
ActiveMQ Apache的一個子專案,能夠以代理人和點對點的技術實現佇列

4.使用分散式服務實現業務的複用

隨著業務不斷增加,後臺系統由一個單一應用膨脹為一個巨無霸系統,系統中聚合了大量的應用和服務,各個模組之間有很多功能重複實現(例如登入模組),造成了開發、運維、部署的麻煩。

膨脹的系統

大量應用中的重複模組會帶來大量的訪問,而每個應用與資料庫的連線,一般是使用資料庫的連線池,這個連線池的資源一般是不釋放且一直保留著。假設連線池中有10個連線,中一個數百的伺服器叢集中,就佔用了資料庫1000個連線。資料庫中的每個連線都是十分珍貴的資源,在資源有限的情況下,這裡被佔用了,其他能用的資源就少了。

解決這些問題的方法就是把重複實現的模組獨立部署為遠端服務,新增的業務呼叫遠端服務所提供的功能實現相關的業務,不依賴於裡面具體的程式碼實現。

使用遠端服務後的系統

實現遠端服務可以 參考 REST設計原則 和 RPC遠端呼叫協議。

開源的RPC庫有:

開源的RPC庫 說明
Hprose 輕量級、跨語言、跨平臺、無侵入式、高效能動態遠端物件呼叫引擎庫
Dubbo 分散式服務框架,致力於提供高效能和透明化的RPC遠端呼叫服務和SOA服務治理方案

5.使用者驗證方案最佳實踐

App操作中經常涉及使用者登入操作,登入就需要使用到使用者名稱和密碼,為了安全起見,在登入過程中暴漏密碼的次數越少越好。

1.使用HTTPS協議

HTTPS協議是 HTTP協議 和 SSL/TLS協議 的組合。其是一個安全通訊通道,基於HTTP開發,用於在客戶計算機和App後臺之間交換資訊。其使用安全套接字層(SSL)進行資訊交換,簡單來說就是HTTP的安全版。

HTTPS實際上應用了安全套接字層(SSL)作為HTTP應用層的子層。

HTTPS的模型:

HTTP
SSL/TLS(安全套接字層/傳輸層安全協議)
TCP
IP
網路傳輸

避免資訊的洩漏,最基本的方案是所有涉及安全性的API請求都必須使用HTTPS協議。

2.選擇JSON作為資料交換格式

JSON是一種輕量級的資料交換格式,採用完全獨立於語言的文字格式,易於編寫,也易於機器解析和生成,而且對比XML更省流量,這些特性使得JSON成為理想的資料交換語言。

3.基本的使用者驗證方案

傳統Web網站使用Cookie+Session保持使用者的登入狀態,App後臺則使用token進行驗證,流程如下:

App後臺基本的使用者登入方案

此時App已經獲取到了token值,為了安全,我們不在網路上傳輸token,而使用簽名校驗(這裡使用URL簽名)的方式,API請求加上URL簽名sign和使用者id後如下:

test.com/user/update?uid=2&sign=3f1e736bc4ae958ae7e8500b45aefdbb&age=22

這樣,token就不需要附在URL上了。App後臺簽名校驗流程如下:

App後臺簽名校驗流程

還有的童鞋喜歡設定時間戳,這樣時間一長,URL就失效了,也是一種不錯的進一步的優化方案。

建議:為了保障資料安全,這裡建議 同時使用 HTTPS 和 簽名校驗

6.App後臺架構的演進原則

App後臺的架構是由業務規模驅動而演進的,App後臺是為業務服務的,App後臺的價值在於能為業務提供其所需要的功能,不應過度設計。

從專案的角度,當App訪問量不大時,應該快速搭建App後臺,讓App儘快上線給使用者提供服務,驗證商業模式的正確性,同時快速迭代產品。

當App訪問量不斷上升,這時要在保證快速迭代的前提下,同時兼顧高效能和高可用。

當App訪問量達到一定階段後,增長曲線就會放緩,但業務變得更加複雜,對高效能和高可用的要求也更高,效能問題、模組間的耦合、程式碼的複雜性會更加突出和明顯,這時要使用業務拆分、分散式服務呼叫,甚至是技術轉型等問題。

1.專案啟動時——單機部署

我們看一個App後臺極簡化的架構:

App後臺極簡化的架構

一開始就使用Redis的好處:

既能用作快取,又能充當佇列服務,而且併發效能高,能在長時間內應對業務壓力,非常適合初期的專案。

這裡使用Redis驗證使用者資訊,充當訊息佇列。

而檔案服務初期可以選擇 檔案雲端儲存服務,或者自己搭建一個資源伺服器。

2.專案一定規模時——分散式部署

我們看一個百萬級到千萬級的架構:

百萬級到千萬級的架構

這裡新增了專門用於連線內部伺服器的SSH服務的外網通道,保證SSH操作隨時可用,同時加入了伺服器叢集,提供負載能力。

隨著業務的發展,某些資料表的規模會以幾何級增長,當資料達到一定規模時,查詢讀取效能就下降的厲害,資料庫主從的架構不能應對業務上的讀寫壓力,這時架構上要考慮分表(水平拆分/垂直拆分)。

當業務繼續不斷髮展,資料庫分表後的讀寫效能也可能沒法滿足業務上的需求,這時只能採用進一步的拆分策略——分庫。用 Cobar 或者 MyCat 等關係型資料等分散式處理系統後,分庫後的架構如下:

分表分庫的架構

下來看一個真實社交App專案所採用的後臺架構方案:

7.社交App後臺架構設計方案分享

場景:類似 微博,使用者與使用者之間存在關注/粉絲兩種關係,一個使用者發表了新內容,關注他的使用者也能在個人主頁上收到最新的動態。類似 微博 這種場景:

weibo

社交核心功能是 Feed(指使用者通過關注,聚合了被關注使用者的最新的內容,也包含自己的內容,以供自己瀏覽的資訊服務)。

1.Feed基本表結構

常見的Feed架構是把資料儲存在MySQL,熱點資料儲存(一般最近3天)在快取(Redis/Memcached),保證絕大多數請求通過快取直接返回,只有少量請求穿透快取落到資料庫。

下面看一下最簡單的Feed表結構:

send_content:傳送內容表,儲存使用者發表的內容:

欄位 說明
feed_id 發表的feed的id,主鍵自增
author_id 發表該feed的使用者id
content feed的內容

reveive_content:接收內容表,用於推模式時儲存使用者接收的內容:

欄位 說明
feed_id 發表的feed的id,主鍵自增
author_id 發表該feed的使用者id
reveive_id 接收該feed的使用者id
content feed的內容

followings:關注表,儲存使用者關注的人:

欄位 說明
id 主鍵自增
uid 使用者id
following_id 該使用者關注的其他使用者id

followers:粉絲表,儲存使用者的粉絲:

欄位 說明
id 主鍵自增
uid 使用者id
follower_id 關注該使用者的使用者id

2.Feed推拉模式——推模式使用者發表一條內容的流程

1)uid為1的使用者發表一條內容 “HelloWorld” 資訊。 
2)這條內容寫入傳送內容表 “send_content” 後內容如下:

feed_id author_id content
1 1 HelloWorld

3)在粉絲表 “followers” 查詢uid為1使用者的粉絲,粉絲表 “followers” 的內容如下:

id uid follower_id
1 1 2

可知,id為1使用者的粉絲是id為2的使用者。

4)因為id為2的使用者的feed中需要顯示這條內容,因此把內容寫入接收內容表 “reveive_content”,寫入後接受內容表 “reveive_content” 內容如下:

feed_id author_id reveive_id content
1 1 2 HelloWorld

5)當id為2的使用者顯示feed時,通過sql語句 “select * from reveive_content where reveive_id=2” 就能查詢該使用者需要顯示的資料了。

推模式的缺點是:

推送人數過大會出現延時,而且浪費儲存空間;
更新操作成本大,不但變更 “send_content” 表,而且需要同步變更 “reveive_content” 表。

3.Feed推拉模式——拉模式使用者發表一條內容的流程:

1)uid為5的使用者發表一條內容 “Thinks” 資訊。 
2)這條內容寫入傳送內容表 “send_content” 後內容如下:

feed_id author_id content
1 1 HelloWorld
2 5 Thinks

3)當uid為10的使用者顯示feed時,在關注表 “followings” 查詢uid為10所關注的使用者,關注表如下:

id uid following_id
1 10 5

可知,uid為10的使用者關注了uid為5的使用者,因此需要獲取uid為5的使用者發表的內容。

4)uid為5的使用者通過sql語句 “select * from send_content where author_id in (5)” 查詢所以需要顯示的內容。

由上述可知,拉模式採用了時間換空間的策略,使用者推送內容時效率很高,但當用戶顯示feed時,需要花費大量的時間在聚合運算上。

總結:

- 發表內容 顯示feed 變更通知
推模式 推送給所有粉絲 一個sql語句就能完成 變更成本高
拉模式 不推送 需要大量的聚合運算 無變更成本

像 “微博” 中公開的微博採用拉模式,私密性的微博採用推模式。

拉模式最大的問題就是大量的聚合運算,請求的響應時間可能較長,可以通過快取策略讓大部分的請求的響應時間達到2到3毫秒。

8.其他的一些經驗

1.高效更新資料——內容的推拉

平常App設計中,如果App需要知道首頁是否有內容更新,通過一個輪詢機制訪問獲取資料API,從API是否返回更新的資料得知是否有內容更新,輪詢上很典型的拉模式,但是耗電、耗流量。

怎麼減少輪詢呢? 這裡給出解決方案是推模式,如下圖:

內容的推拉

當然不能只用推模式,因為手機環境的複雜性,不能保證資料更新的通知一定能夠到達App,所以也要採用輪詢的方式定期拉資料,時間間隔設定可以相對長一點,通過這種推拉結合的模式,就能大大減少App訪問App後臺的頻率和傳輸的資料量。

2.處理表情的一些技巧

表情在MySQL的儲存,表情UTF-8編碼有的是3個位元組,有的是4個位元組,所以一般的UTF編碼(3個位元組)是無法儲存表情資料的,常用的解決方案是:

把MySQL升級到5.5以上,然後把字元編碼改為utf8mb4_general_ci。

3.可供選擇的成熟穩定的開源軟體

功能 可供選擇的開源軟體
專案管理軟體 Mantis、BugFree
程式碼管理軟體 SVN、Git
程式語言 Java、PHP、Python等
伺服器系統 CentOS、Ubuntu
HTTP/HTTPS伺服器 Nginx、Tomcat、Apache
負載均衡 Nginx、LVS、HAProxy
郵件服務 Postfix、Sendmail
訊息佇列 RabbitMQ、ZeroMQ、Redis
檔案系統 Fastdfs、mogileFS、TFS
Android推送 Androidpn、gopush
IOS推送 Javapns、Pyapns
地理位置查詢LBS MongoDB
聊天 Openfire、ejobberd
監控 ngiOS、zabbix
快取 Memcache、Redis
關係型資料庫 MySQL、MariaDB、PostgreSQL
NoSQL資料庫 Redis、MongoDB、Cassandra
搜尋 Coreseek、Solr、ElasticSearch
圖片處理 GraphicsMagick、ImageMagick
分散式訪問服務 dubbo、dubbox

3.可供選擇的成熟可靠的雲服務

對於初創公司還是建議儘可能的使用成熟可靠的雲服務和開源軟體,自身只專注於業務邏輯。

功能 可供選擇的雲服務
專案管理工具 Teambition、Tower
程式碼託管平臺 GitHub、Gitlab、Bitbucket、CSDN CODE、Coding
負載均衡 阿里雲SLB、騰訊雲CLB
郵件服務 SendCloud、MailGun
訊息佇列 阿里雲MNS、騰訊雲CMQ
檔案系統、圖片處理 七牛雲、阿里雲物件儲存OSS、騰訊雲物件儲存COS
Android推送 極光、個推、百度推送
IOS推送 極光、個推、百度推送
聊天 融雲、環信
監控 監控寶、雲伺服器自帶的監控服務
快取 阿里雲快取服務、騰訊雲彈性快取
關係型資料庫 阿里雲RDS、騰訊雲CDB
NoSQL資料庫 阿里雲NoSQL產品、騰訊雲NoSQL產品
搜尋 阿里雲開放搜尋、騰訊雲搜TCS
分散式訪問服務 阿里雲EDAS
防火牆 阿里云云盾、騰訊雲安全
簡訊傳送 shareSDK、bmob、Luosimao
社交登入分享 shareSDK

最後,在移動網際網路專案中,產品的研發講求 小步快走,快速迭代。 架構的設計也可以遵循同樣的思路,喜歡本文的記得 頂 一下哦!