轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/smartbetter/article/details/53933096
做App做的久了,就想研究一下與之相關(guān)的App后臺(tái),發(fā)現(xiàn)也是蠻有趣的。App后臺(tái)的兩個(gè)重要作用就是 遠(yuǎn)程存儲(chǔ)數(shù)據(jù) 和 消息中轉(zhuǎn)。這里面的知識(shí)體系也是相當(dāng)復(fù)雜,做好一個(gè)App后臺(tái)也是需要長(zhǎng)期錘煉的。本篇文章從 App 后臺(tái)架構(gòu) 的角度介紹。好了,下面進(jìn)入正題:
說起架構(gòu),我們先看一下何為架構(gòu),百度百科是這樣說的:架構(gòu),又名軟件架構(gòu),是有關(guān)軟件整體結(jié)構(gòu)與組件的抽象描述,用于指導(dǎo)大型軟件系統(tǒng)各個(gè)方面的設(shè)計(jì)。那么我們也可以看出,架構(gòu)是和業(yè)務(wù)緊密相關(guān)的,是由業(yè)務(wù)驅(qū)動(dòng)的。
由于App客戶端的特性,因此App后臺(tái)對(duì)技術(shù)實(shí)現(xiàn)和一般的Web后臺(tái)是有區(qū)別的。首先看一個(gè)適合App開發(fā)的開發(fā)模式:
這里推薦Scrum這個(gè)敏捷開發(fā)框架,具體可以查看Scrum官網(wǎng)學(xué)習(xí)使用,這里只是引入。
Scrum流程如下圖:
數(shù)據(jù)庫(kù)產(chǎn)品眾多,這里我就針對(duì)Redis、MongoDB、MySQL還有MySQL的分支MariaDB展開說明:
數(shù)據(jù)庫(kù) | 數(shù)據(jù)存放位置 | 查找數(shù)據(jù)的區(qū)別 |
---|---|---|
Redis | 內(nèi)存 | 基于鍵值對(duì)存儲(chǔ),讀寫速度快 |
MongoDB | 同時(shí)使用了硬盤和內(nèi)存 | 每個(gè)數(shù)據(jù)有一個(gè)id(索引),知道id(索引)查詢速度快,不知道id(索引)效率低 |
MySQL(MongoDB) | 硬盤 | 每個(gè)數(shù)據(jù)有一個(gè)id(索引),知道id(索引)查詢速度快,不知道id(索引)效率低 |
然后根據(jù)不同的產(chǎn)品需求選擇恰當(dāng)?shù)臄?shù)據(jù)庫(kù)產(chǎn)品,如果沒有特殊的需求,Redis做緩存系統(tǒng),MySQL 或 MariaDB 做數(shù)據(jù)庫(kù)(常見的設(shè)置是 數(shù)據(jù)庫(kù)默認(rèn)字符集utf8,默認(rèn)排序utf8_general_ci) 將會(huì)是很好的選擇。
軟件優(yōu)化:
1)正確使用MyISAM和InnoDB存儲(chǔ)引擎2)正確使用索引3)避免使用 select *4)字段盡可能的設(shè)置 非NULL
硬件優(yōu)化:
1)增加物理內(nèi)存2)增加應(yīng)用緩存3)使用SSD硬盤
架構(gòu)優(yōu)化:
1)分表2)讀寫分離
3)分庫(kù)(把一張表的數(shù)據(jù)分別存儲(chǔ)在不同的數(shù)據(jù)庫(kù),可用MyCat實(shí)現(xiàn),MyCat,關(guān)系型數(shù)據(jù)庫(kù)分布式處理軟件)。MyCat以代理服務(wù)器的形式位于App服務(wù)器和后臺(tái)數(shù)據(jù)庫(kù)之間,對(duì)外開放的接口是MySQL通信協(xié)議,將App服務(wù)器傳過來的sql語(yǔ)句按照路由的規(guī)則拆解轉(zhuǎn)發(fā)到不同的后臺(tái)數(shù)據(jù)庫(kù),并把結(jié)果匯總返回。MyCat部署模型如下:
CentOS 則是一個(gè)不錯(cuò)的選擇。關(guān)于服務(wù)器的部署,我在之前已經(jīng)介紹過了,地址如下:
Nginx + Tomcat 反向代理 負(fù)載均衡 集群 部署指南
http://blog.csdn.net/smartbetter/article/details/53535435
Nginx + Tomcat 反向代理 如何在高效的在一臺(tái)服務(wù)器部署多個(gè)站點(diǎn)
http://blog.csdn.net/smartbetter/article/details/53615313
下面補(bǔ)充兩個(gè)常見的Linux命令:
top 顯示系統(tǒng)資源情況netstat 查看網(wǎng)絡(luò)相關(guān)信息
當(dāng)后臺(tái)系統(tǒng)發(fā)現(xiàn)完成某些小任務(wù)需要花費(fèi)很多時(shí)間,而且遲點(diǎn)晚成也不影響整個(gè)任務(wù)的完成進(jìn)度時(shí),就會(huì)把這些小任務(wù)交給消息隊(duì)列。例如發(fā)送郵件、短信、推送消息等任務(wù)都非常適合在消息隊(duì)列中處理。
把這些任務(wù)放在消息隊(duì)列中,可加快App后臺(tái)請(qǐng)求都響應(yīng)時(shí)間。同時(shí)消息隊(duì)列也能把大量的并發(fā)請(qǐng)求變成串行的請(qǐng)求,來減輕服務(wù)器的負(fù)擔(dān)。
常見的消息隊(duì)列軟件有:
消息隊(duì)列軟件 | 說明 |
---|---|
RabbitMQ | 重量級(jí),適合企業(yè)級(jí)的開發(fā),自帶Web監(jiān)控界面,方便監(jiān)控隊(duì)列的情況 |
Redis | 輕量級(jí),是一個(gè)key-value系統(tǒng),但是也支持消息隊(duì)列這種數(shù)據(jù)結(jié)構(gòu),App后臺(tái)中Redis被廣泛使用 |
ZeroMQ | 號(hào)稱最快,尤其針對(duì)大吞吐量的需求場(chǎng)景 |
ActiveMQ | Apache的一個(gè)子項(xiàng)目,能夠以代理人和點(diǎn)對(duì)點(diǎn)的技術(shù)實(shí)現(xiàn)隊(duì)列 |
隨著業(yè)務(wù)不斷增加,后臺(tái)系統(tǒng)由一個(gè)單一應(yīng)用膨脹為一個(gè)巨無霸系統(tǒng),系統(tǒng)中聚合了大量的應(yīng)用和服務(wù),各個(gè)模塊之間有很多功能重復(fù)實(shí)現(xiàn)(例如登錄模塊),造成了開發(fā)、運(yùn)維、部署的麻煩。
大量應(yīng)用中的重復(fù)模塊會(huì)帶來大量的訪問,而每個(gè)應(yīng)用與數(shù)據(jù)庫(kù)的連接,一般是使用數(shù)據(jù)庫(kù)的連接池,這個(gè)連接池的資源一般是不釋放且一直保留著。假設(shè)連接池中有10個(gè)連接,中一個(gè)數(shù)百的服務(wù)器集群中,就占用了數(shù)據(jù)庫(kù)1000個(gè)連接。數(shù)據(jù)庫(kù)中的每個(gè)連接都是十分珍貴的資源,在資源有限的情況下,這里被占用了,其他能用的資源就少了。
解決這些問題的方法就是把重復(fù)實(shí)現(xiàn)的模塊獨(dú)立部署為遠(yuǎn)程服務(wù),新增的業(yè)務(wù)調(diào)用遠(yuǎn)程服務(wù)所提供的功能實(shí)現(xiàn)相關(guān)的業(yè)務(wù),不依賴于里面具體的代碼實(shí)現(xiàn)。
實(shí)現(xiàn)遠(yuǎn)程服務(wù)可以 參考 REST設(shè)計(jì)原則 和 RPC遠(yuǎn)程調(diào)用協(xié)議。
開源的RPC庫(kù)有:
開源的RPC庫(kù) | 說明 |
---|---|
Hprose | 輕量級(jí)、跨語(yǔ)言、跨平臺(tái)、無侵入式、高性能動(dòng)態(tài)遠(yuǎn)程對(duì)象調(diào)用引擎庫(kù) |
Dubbo | 分布式服務(wù)框架,致力于提供高性能和透明化的RPC遠(yuǎn)程調(diào)用服務(wù)和SOA服務(wù)治理方案 |
App操作中經(jīng)常涉及用戶登錄操作,登錄就需要使用到用戶名和密碼,為了安全起見,在登錄過程中暴漏密碼的次數(shù)越少越好。
HTTPS協(xié)議是 HTTP協(xié)議 和 SSL/TLS協(xié)議 的組合。其是一個(gè)安全通信通道,基于HTTP開發(fā),用于在客戶計(jì)算機(jī)和App后臺(tái)之間交換信息。其使用安全套接字層(SSL)進(jìn)行信息交換,簡(jiǎn)單來說就是HTTP的安全版。
HTTPS實(shí)際上應(yīng)用了安全套接字層(SSL)作為HTTP應(yīng)用層的子層。
HTTPS的模型:
HTTP |
---|
SSL/TLS(安全套接字層/傳輸層安全協(xié)議) |
TCP |
IP |
網(wǎng)絡(luò)傳輸 |
避免信息的泄漏,最基本的方案是所有涉及安全性的API請(qǐng)求都必須使用HTTPS協(xié)議。
JSON是一種輕量級(jí)的數(shù)據(jù)交換格式,采用完全獨(dú)立于語(yǔ)言的文本格式,易于編寫,也易于機(jī)器解析和生成,而且對(duì)比XML更省流量,這些特性使得JSON成為理想的數(shù)據(jù)交換語(yǔ)言。
傳統(tǒng)Web網(wǎng)站使用Cookie+Session保持用戶的登錄狀態(tài),App后臺(tái)則使用token進(jìn)行驗(yàn)證,流程如下:
此時(shí)App已經(jīng)獲取到了token值,為了安全,我們不在網(wǎng)絡(luò)上傳輸token,而使用簽名校驗(yàn)(這里使用URL簽名)的方式,API請(qǐng)求加上URL簽名sign和用戶id后如下:
test.com/user/update?uid=2&sign=3f1e736bc4ae958ae7e8500b45aefdbb&age=22
這樣,token就不需要附在URL上了。App后臺(tái)簽名校驗(yàn)流程如下:
還有的童鞋喜歡設(shè)置時(shí)間戳,這樣時(shí)間一長(zhǎng),URL就失效了,也是一種不錯(cuò)的進(jìn)一步的優(yōu)化方案。
建議:為了保障數(shù)據(jù)安全,這里建議 同時(shí)使用 HTTPS 和 簽名校驗(yàn)。
App后臺(tái)的架構(gòu)是由業(yè)務(wù)規(guī)模驅(qū)動(dòng)而演進(jìn)的,App后臺(tái)是為業(yè)務(wù)服務(wù)的,App后臺(tái)的價(jià)值在于能為業(yè)務(wù)提供其所需要的功能,不應(yīng)過度設(shè)計(jì)。
從項(xiàng)目的角度,當(dāng)App訪問量不大時(shí),應(yīng)該快速搭建App后臺(tái),讓App盡快上線給用戶提供服務(wù),驗(yàn)證商業(yè)模式的正確性,同時(shí)快速迭代產(chǎn)品。
當(dāng)App訪問量不斷上升,這時(shí)要在保證快速迭代的前提下,同時(shí)兼顧高性能和高可用。
當(dāng)App訪問量達(dá)到一定階段后,增長(zhǎng)曲線就會(huì)放緩,但業(yè)務(wù)變得更加復(fù)雜,對(duì)高性能和高可用的要求也更高,性能問題、模塊間的耦合、代碼的復(fù)雜性會(huì)更加突出和明顯,這時(shí)要使用業(yè)務(wù)拆分、分布式服務(wù)調(diào)用,甚至是技術(shù)轉(zhuǎn)型等問題。
我們看一個(gè)App后臺(tái)極簡(jiǎn)化的架構(gòu):
一開始就使用Redis的好處:
既能用作緩存,又能充當(dāng)隊(duì)列服務(wù),而且并發(fā)性能高,能在長(zhǎng)時(shí)間內(nèi)應(yīng)對(duì)業(yè)務(wù)壓力,非常適合初期的項(xiàng)目。
這里使用Redis驗(yàn)證用戶信息,充當(dāng)消息隊(duì)列。
而文件服務(wù)初期可以選擇 文件云存儲(chǔ)服務(wù),或者自己搭建一個(gè)資源服務(wù)器。
我們看一個(gè)百萬級(jí)到千萬級(jí)的架構(gòu):
這里新增了專門用于連接內(nèi)部服務(wù)器的SSH服務(wù)的外網(wǎng)通道,保證SSH操作隨時(shí)可用,同時(shí)加入了服務(wù)器集群,提供負(fù)載能力。
隨著業(yè)務(wù)的發(fā)展,某些數(shù)據(jù)表的規(guī)模會(huì)以幾何級(jí)增長(zhǎng),當(dāng)數(shù)據(jù)達(dá)到一定規(guī)模時(shí),查詢讀取性能就下降的厲害,數(shù)據(jù)庫(kù)主從的架構(gòu)不能應(yīng)對(duì)業(yè)務(wù)上的讀寫壓力,這時(shí)架構(gòu)上要考慮分表(水平拆分/垂直拆分)。
當(dāng)業(yè)務(wù)繼續(xù)不斷發(fā)展,數(shù)據(jù)庫(kù)分表后的讀寫性能也可能沒法滿足業(yè)務(wù)上的需求,這時(shí)只能采用進(jìn)一步的拆分策略——分庫(kù)。用 Cobar 或者 MyCat 等關(guān)系型數(shù)據(jù)等分布式處理系統(tǒng)后,分庫(kù)后的架構(gòu)如下:
下來看一個(gè)真實(shí)社交App項(xiàng)目所采用的后臺(tái)架構(gòu)方案:
場(chǎng)景:類似 微博,用戶與用戶之間存在關(guān)注/粉絲兩種關(guān)系,一個(gè)用戶發(fā)表了新內(nèi)容,關(guān)注他的用戶也能在個(gè)人主頁(yè)上收到最新的動(dòng)態(tài)。類似 微博 這種場(chǎng)景:
社交核心功能是 Feed(指用戶通過關(guān)注,聚合了被關(guān)注用戶的最新的內(nèi)容,也包含自己的內(nèi)容,以供自己瀏覽的信息服務(wù))。
常見的Feed架構(gòu)是把數(shù)據(jù)存儲(chǔ)在MySQL,熱點(diǎn)數(shù)據(jù)存儲(chǔ)(一般最近3天)在緩存(Redis/Memcached),保證絕大多數(shù)請(qǐng)求通過緩存直接返回,只有少量請(qǐng)求穿透緩存落到數(shù)據(jù)庫(kù)。
下面看一下最簡(jiǎn)單的Feed表結(jié)構(gòu):
send_content:發(fā)送內(nèi)容表,存儲(chǔ)用戶發(fā)表的內(nèi)容:
字段 | 說明 |
---|---|
feed_id | 發(fā)表的feed的id,主鍵自增 |
author_id | 發(fā)表該feed的用戶id |
content | feed的內(nèi)容 |
reveive_content:接收內(nèi)容表,用于推模式時(shí)存儲(chǔ)用戶接收的內(nèi)容:
字段 | 說明 |
---|---|
feed_id | 發(fā)表的feed的id,主鍵自增 |
author_id | 發(fā)表該feed的用戶id |
reveive_id | 接收該feed的用戶id |
content | feed的內(nèi)容 |
followings:關(guān)注表,存儲(chǔ)用戶關(guān)注的人:
字段 | 說明 |
---|---|
id | 主鍵自增 |
uid | 用戶id |
following_id | 該用戶關(guān)注的其他用戶id |
followers:粉絲表,存儲(chǔ)用戶的粉絲:
字段 | 說明 |
---|---|
id | 主鍵自增 |
uid | 用戶id |
follower_id | 關(guān)注該用戶的用戶id |
1)uid為1的用戶發(fā)表一條內(nèi)容 “HelloWorld” 信息。
2)這條內(nèi)容寫入發(fā)送內(nèi)容表 “send_content” 后內(nèi)容如下:
feed_id | author_id | content |
---|---|---|
1 | 1 | HelloWorld |
3)在粉絲表 “followers” 查找uid為1用戶的粉絲,粉絲表 “followers” 的內(nèi)容如下:
id | uid | follower_id |
---|---|---|
1 | 1 | 2 |
可知,id為1用戶的粉絲是id為2的用戶。
4)因?yàn)閕d為2的用戶的feed中需要顯示這條內(nèi)容,因此把內(nèi)容寫入接收內(nèi)容表 “reveive_content”,寫入后接受內(nèi)容表 “reveive_content” 內(nèi)容如下:
feed_id | author_id | reveive_id | content |
---|---|---|---|
1 | 1 | 2 | HelloWorld |
5)當(dāng)id為2的用戶顯示feed時(shí),通過sql語(yǔ)句 “select * from reveive_content where reveive_id=2” 就能查詢?cè)撚脩粜枰@示的數(shù)據(jù)了。
推模式的缺點(diǎn)是:
推送人數(shù)過大會(huì)出現(xiàn)延時(shí),而且浪費(fèi)存儲(chǔ)空間;更新操作成本大,不但變更 “send_content” 表,而且需要同步變更 “reveive_content” 表。
1)uid為5的用戶發(fā)表一條內(nèi)容 “Thinks” 信息。
2)這條內(nèi)容寫入發(fā)送內(nèi)容表 “send_content” 后內(nèi)容如下:
feed_id | author_id | content |
---|---|---|
1 | 1 | HelloWorld |
2 | 5 | Thinks |
3)當(dāng)uid為10的用戶顯示feed時(shí),在關(guān)注表 “followings” 查找uid為10所關(guān)注的用戶,關(guān)注表如下:
id | uid | following_id |
---|---|---|
1 | 10 | 5 |
可知,uid為10的用戶關(guān)注了uid為5的用戶,因此需要獲取uid為5的用戶發(fā)表的內(nèi)容。
4)uid為5的用戶通過sql語(yǔ)句 “select * from send_content where author_id in (5)” 查詢所以需要顯示的內(nèi)容。
由上述可知,拉模式采用了時(shí)間換空間的策略,用戶推送內(nèi)容時(shí)效率很高,但當(dāng)用戶顯示feed時(shí),需要花費(fèi)大量的時(shí)間在聚合運(yùn)算上。
總結(jié):
- | 發(fā)表內(nèi)容 | 顯示feed | 變更通知 |
---|---|---|---|
推模式 | 推送給所有粉絲 | 一個(gè)sql語(yǔ)句就能完成 | 變更成本高 |
拉模式 | 不推送 | 需要大量的聚合運(yùn)算 | 無變更成本 |
像 “微博” 中公開的微博采用拉模式,私密性的微博采用推模式。
拉模式最大的問題就是大量的聚合運(yùn)算,請(qǐng)求的響應(yīng)時(shí)間可能較長(zhǎng),可以通過緩存策略讓大部分的請(qǐng)求的響應(yīng)時(shí)間達(dá)到2到3毫秒。
平常App設(shè)計(jì)中,如果App需要知道首頁(yè)是否有內(nèi)容更新,通過一個(gè)輪詢機(jī)制訪問獲取數(shù)據(jù)API,從API是否返回更新的數(shù)據(jù)得知是否有內(nèi)容更新,輪詢上很典型的拉模式,但是耗電、耗流量。
怎么減少輪詢呢? 這里給出解決方案是推模式,如下圖:
當(dāng)然不能只用推模式,因?yàn)槭謾C(jī)環(huán)境的復(fù)雜性,不能保證數(shù)據(jù)更新的通知一定能夠到達(dá)App,所以也要采用輪詢的方式定期拉數(shù)據(jù),時(shí)間間隔設(shè)置可以相對(duì)長(zhǎng)一點(diǎn),通過這種推拉結(jié)合的模式,就能大大減少App訪問App后臺(tái)的頻率和傳輸?shù)臄?shù)據(jù)量。
表情在MySQL的存儲(chǔ),表情UTF-8編碼有的是3個(gè)字節(jié),有的是4個(gè)字節(jié),所以一般的UTF編碼(3個(gè)字節(jié))是無法存儲(chǔ)表情數(shù)據(jù)的,常用的解決方案是:
把MySQL升級(jí)到5.5以上,然后把字符編碼改為utf8mb4_general_ci。
功能 | 可供選擇的開源軟件 |
---|---|
項(xiàng)目管理軟件 | Mantis、BugFree |
代碼管理軟件 | SVN、Git |
編程語(yǔ)言 | Java、PHP、Python等 |
服務(wù)器系統(tǒng) | CentOS、Ubuntu |
HTTP/HTTPS服務(wù)器 | Nginx、Tomcat、Apache |
負(fù)載均衡 | Nginx、LVS、HAProxy |
郵件服務(wù) | Postfix、Sendmail |
消息隊(duì)列 | RabbitMQ、ZeroMQ、Redis |
文件系統(tǒng) | Fastdfs、mogileFS、TFS |
Android推送 | Androidpn、gopush |
IOS推送 | Javapns、Pyapns |
地理位置查詢LBS | MongoDB |
聊天 | Openfire、ejobberd |
監(jiān)控 | ngiOS、zabbix |
緩存 | Memcache、Redis |
關(guān)系型數(shù)據(jù)庫(kù) | MySQL、MariaDB、PostgreSQL |
NoSQL數(shù)據(jù)庫(kù) | Redis、MongoDB、Cassandra |
搜索 | Coreseek、Solr、ElasticSearch |
圖片處理 | GraphicsMagick、ImageMagick |
分布式訪問服務(wù) | dubbo、dubbox |
對(duì)于初創(chuàng)公司還是建議盡可能的使用成熟可靠的云服務(wù)和開源軟件,自身只專注于業(yè)務(wù)邏輯。
功能 | 可供選擇的云服務(wù) |
---|---|
項(xiàng)目管理工具 | Teambition、Tower |
代碼托管平臺(tái) | GitHub、Gitlab、Bitbucket、CSDN CODE、Coding |
負(fù)載均衡 | 阿里云SLB、騰訊云CLB |
郵件服務(wù) | SendCloud、MailGun |
消息隊(duì)列 | 阿里云MNS、騰訊云CMQ |
文件系統(tǒng)、圖片處理 | 七牛云、阿里云對(duì)象存儲(chǔ)OSS、騰訊云對(duì)象存儲(chǔ)COS |
Android推送 | 極光、個(gè)推、百度推送 |
IOS推送 | 極光、個(gè)推、百度推送 |
聊天 | 融云、環(huán)信 |
監(jiān)控 | 監(jiān)控寶、云服務(wù)器自帶的監(jiān)控服務(wù) |
緩存 | 阿里云緩存服務(wù)、騰訊云彈性緩存 |
關(guān)系型數(shù)據(jù)庫(kù) | 阿里云RDS、騰訊云CDB |
NoSQL數(shù)據(jù)庫(kù) | 阿里云NoSQL產(chǎn)品、騰訊云NoSQL產(chǎn)品 |
搜索 | 阿里云開放搜索、騰訊云搜TCS |
分布式訪問服務(wù) | 阿里云EDAS |
防火墻 | 阿里云云盾、騰訊云安全 |
短信發(fā)送 | shareSDK、bmob、Luosimao |
社交登錄分享 | shareSDK |
最后,在移動(dòng)互聯(lián)網(wǎng)項(xiàng)目中,產(chǎn)品的研發(fā)講求 小步快走,快速迭代。 架構(gòu)的設(shè)計(jì)也可以遵循同樣的思路,喜歡本文的記得 頂 一下哦!
聯(lián)系客服