App 後臺架構設計方案 設計思想與最佳實踐
做App做的久了,就想研究一下與之相關的App後臺,發現也是蠻有趣的。App後臺的兩個重要作用就是 遠端儲存資料 和 訊息中轉。這裡面的知識體系也是相當複雜,做好一個App後臺也是需要長期錘鍊的。本篇文章從 App 後臺架構 的角度介紹。好了,下面進入正題:
說起架構,我們先看一下何為架構,百度百科是這樣說的:架構,又名軟體架構,是有關軟體整體結構與元件的抽象描述,用於指導大型軟體系統各個方面的設計。那麼我們也可以看出,架構是和業務緊密相關的,是由業務驅動的。
由於App客戶端的特性,因此App後臺對技術實現和一般的Web後臺是有區別的。首先看一個適合App開發的開發模式:
1.敏捷開發模式
這裡推薦Scrum這個敏捷開發框架,具體可以檢視Scrum官網學習使用,這裡只是引入。
Scrum流程如下圖:
2.選擇合適的資料庫產品和伺服器系統
資料庫產品眾多,這裡我就針對Redis、MongoDB、MySQL還有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部署模型如下:
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已經獲取到了token值,為了安全,我們不在網路上傳輸token,而使用簽名校驗(這裡使用URL簽名)的方式,API請求加上URL簽名sign和使用者id後如下:
test.com/user/update?uid=2&sign=3f1e736bc4ae958ae7e8500b45aefdbb&age=22
這樣,token就不需要附在URL上了。App後臺簽名校驗流程如下:
還有的童鞋喜歡設定時間戳,這樣時間一長,URL就失效了,也是一種不錯的進一步的優化方案。
建議:為了保障資料安全,這裡建議 同時使用 HTTPS 和 簽名校驗。
6.App後臺架構的演進原則
App後臺的架構是由業務規模驅動而演進的,App後臺是為業務服務的,App後臺的價值在於能為業務提供其所需要的功能,不應過度設計。
從專案的角度,當App訪問量不大時,應該快速搭建App後臺,讓App儘快上線給使用者提供服務,驗證商業模式的正確性,同時快速迭代產品。
當App訪問量不斷上升,這時要在保證快速迭代的前提下,同時兼顧高效能和高可用。
當App訪問量達到一定階段後,增長曲線就會放緩,但業務變得更加複雜,對高效能和高可用的要求也更高,效能問題、模組間的耦合、程式碼的複雜性會更加突出和明顯,這時要使用業務拆分、分散式服務呼叫,甚至是技術轉型等問題。
1.專案啟動時——單機部署
我們看一個App後臺極簡化的架構:
一開始就使用Redis的好處:
既能用作快取,又能充當佇列服務,而且併發效能高,能在長時間內應對業務壓力,非常適合初期的專案。
這裡使用Redis驗證使用者資訊,充當訊息佇列。
而檔案服務初期可以選擇 檔案雲端儲存服務,或者自己搭建一個資源伺服器。
2.專案一定規模時——分散式部署
我們看一個百萬級到千萬級的架構:
這裡新增了專門用於連線內部伺服器的SSH服務的外網通道,保證SSH操作隨時可用,同時加入了伺服器叢集,提供負載能力。
隨著業務的發展,某些資料表的規模會以幾何級增長,當資料達到一定規模時,查詢讀取效能就下降的厲害,資料庫主從的架構不能應對業務上的讀寫壓力,這時架構上要考慮分表(水平拆分/垂直拆分)。
當業務繼續不斷髮展,資料庫分表後的讀寫效能也可能沒法滿足業務上的需求,這時只能採用進一步的拆分策略——分庫。用 Cobar 或者 MyCat 等關係型資料等分散式處理系統後,分庫後的架構如下:
下來看一個真實社交App專案所採用的後臺架構方案:
7.社交App後臺架構設計方案分享
場景:類似 微博,使用者與使用者之間存在關注/粉絲兩種關係,一個使用者發表了新內容,關注他的使用者也能在個人主頁上收到最新的動態。類似 微博 這種場景:
社交核心功能是 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 |
最後,在移動網際網路專案中,產品的研發講求 小步快走,快速迭代。 架構的設計也可以遵循同樣的思路,喜歡本文的記得 頂 一下哦!