App 背景架構設計方案 設計思想與最佳實踐
轉載請注明出處:http://blog.csdn.net/smartbetter/article/details/53933096
做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)讀寫分離
-
分庫(把一張表的資料分别存儲在不同的資料庫,可用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
這樣,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 |
最後,在移動網際網路項目中,産品的研發講求 小步快走,快速疊代。 架構的設計也可以遵循同樣的思路,喜歡本文的記得 頂 一下哦!