做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 則是一個不錯的選擇。關於服務器的部署,我在之前已經介紹過了,地址如下:
Nginx + Tomcat 反向代理 負載均衡 集群 部署指南
http://blog.csdn.net/smartbetter/article/details/53535435
Nginx + Tomcat 反向代理 如何在高效的在一台服務器部署多個站點
http://blog.csdn.net/smartbetter/article/details/53615313
下面補充兩個常見的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
- 1
這樣,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 |
最后,在移動互聯網項目中,產品的研發講求 小步快走,快速迭代。 架構的設計也可以遵循同樣的思路,喜歡本文的記得 頂 一下哦!
轉自:
http://blog.csdn.net/smartbetter/article/details/53933096
https://blog.csdn.net/yangzhongxuan/article/details/77949315