當(dāng)一個Web系統(tǒng)從日訪問量10萬逐步增長到1000萬,甚至超過1億的過程中僚饭,Web系統(tǒng)承受的壓力會越來越大震叮,在這個過程中,我們會遇到很多的問題鳍鸵。為了解決這些性能壓力帶來問題苇瓣,我們需要在Web系統(tǒng)架構(gòu)層面搭建多個層次的緩存機制。在不同的壓力階段偿乖,我們會遇到不同的問題击罪,通過搭建不同的服務(wù)和架構(gòu)來解決。
Web負載均衡
Web負載均衡(Load Balancing)贪薪,簡單地說就是給我們的服務(wù)器集群分配“工作任務(wù)”媳禁,而采用恰當(dāng)?shù)姆峙浞绞剑瑢τ诒Wo處于后端的Web服務(wù)器來說画切,非常重要竣稽。
負載均衡的策略有很多,我們從簡單的講起哈霍弹。
1.?HTTP重定向
當(dāng)用戶發(fā)來請求的時候毫别,Web服務(wù)器通過修改HTTP響應(yīng)頭中的Location標(biāo)記來返回一個新的url,然后瀏覽器再繼續(xù)請求這個新url典格,實際上就是頁面重定向岛宦。通過重定向,來達到“負載均衡”的目標(biāo)耍缴。例如砾肺,我們在下載PHP源碼包的時候,點擊下載鏈接時防嗡,為了解決不同國家和地域下載速度的問題债沮,它會返回一個離我們近的下載地址。重定向的HTTP返回碼是302本鸣,如下圖:
如果使用PHP代碼來實現(xiàn)這個功能,方式如下:
這個重定向非常容易實現(xiàn)硅蹦,并且可以自定義各種策略荣德。但是,它在大規(guī)模訪問量下童芹,性能不佳涮瞻。而且,給用戶的體驗也不好假褪,實際請求發(fā)生重定向署咽,增加了網(wǎng)絡(luò)延時。
2. 反向代理負載均衡
反向代理服務(wù)的核心工作主要是轉(zhuǎn)發(fā)HTTP請求,扮演了瀏覽器端和后臺Web服務(wù)器中轉(zhuǎn)的角色宁否。因為它工作在HTTP層(應(yīng)用層)窒升,也就是網(wǎng)絡(luò)七層結(jié)構(gòu)中的第七層,因此也被稱為“七層負載均衡”慕匠”バ耄可以做反向代理的軟件很多,比較常見的一種是Nginx台谊。
Nginx是一種非常靈活的反向代理軟件蓉媳,可以自由定制化轉(zhuǎn)發(fā)策略,分配服務(wù)器流量的權(quán)重等锅铅。反向代理中酪呻,常見的一個問題,就是Web服務(wù)器存儲的session數(shù)據(jù)盐须,因為一般負載均衡的策略都是隨機分配請求的玩荠。同一個登錄用戶的請求,無法保證一定分配到相同的Web機器上丰歌,會導(dǎo)致無法找到session的問題姨蟋。
解決方案主要有兩種:
配置反向代理的轉(zhuǎn)發(fā)規(guī)則,讓同一個用戶的請求一定落到同一臺機器上(通過分析cookie)立帖,復(fù)雜的轉(zhuǎn)發(fā)規(guī)則將會消耗更多的CPU眼溶,也增加了代理服務(wù)器的負擔(dān)。
將session這類的信息晓勇,專門用某個獨立服務(wù)來存儲堂飞,例如redis/memchache,這個方案是比較推薦的绑咱。
反向代理服務(wù)绰筛,也是可以開啟緩存的,如果開啟了描融,會增加反向代理的負擔(dān)铝噩,需要謹(jǐn)慎使用。這種負載均衡策略實現(xiàn)和部署非常簡單窿克,而且性能表現(xiàn)也比較好骏庸。但是,它有“單點故障”的問題年叮,如果掛了具被,會帶來很多的麻煩。而且只损,到了后期Web服務(wù)器繼續(xù)增加一姿,它本身可能成為系統(tǒng)的瓶頸。
3. IP負載均衡
IP負載均衡服務(wù)是工作在網(wǎng)絡(luò)層(修改IP)和傳輸層(修改端口,第四層)叮叹,比起工作在應(yīng)用層(第七層)性能要高出非常多艾栋。原理是第队,他是對IP層的數(shù)據(jù)包的IP地址和端口信息進行修改温自,達到負載均衡的目的泰偿。這種方式休涤,也被稱為“四層負載均衡”缩功。常見的負載均衡方式傀广,是LVS(Linux Virtual Server拢驾,Linux虛擬服務(wù))铐料,通過IPVS(IP Virtual Server噪叙,IP虛擬服務(wù))來實現(xiàn)矮锈。
在負載均衡服務(wù)器收到客戶端的IP包的時候,會修改IP包的目標(biāo)IP地址或端口睁蕾,然后原封不動地投遞到內(nèi)部網(wǎng)絡(luò)中苞笨,數(shù)據(jù)包會流入到實際Web服務(wù)器。實際服務(wù)器處理完成后子眶,又會將數(shù)據(jù)包投遞回給負載均衡服務(wù)器瀑凝,它再修改目標(biāo)IP地址為用戶IP地址,最終回到客戶端臭杰。
上述的方式叫LVS-NAT粤咪,除此之外,還有LVS-RD(直接路由)渴杆,LVS-TUN(IP隧道)寥枝,三者之間都屬于LVS的方式,但是有一定的區(qū)別磁奖,篇幅問題囊拜,不贅敘。
IP負載均衡的性能要高出Nginx的反向代理很多比搭,它只處理到傳輸層為止的數(shù)據(jù)包冠跷,并不做進一步的組包,然后直接轉(zhuǎn)發(fā)給實際服務(wù)器身诺。不過蔽莱,它的配置和搭建比較復(fù)雜。
4. DNS負載均衡
DNS(Domain Name System)負責(zé)域名解析的服務(wù)戚长,域名url實際上是服務(wù)器的別名,實際映射是一個IP地址怠苔,解析過程同廉,就是DNS完成域名到IP的映射。而一個域名是可以配置成對應(yīng)多個IP的。因此迫肖,DNS也就可以作為負載均衡服務(wù)锅劝。
這種負載均衡策略,配置簡單蟆湖,性能極佳故爵。但是,不能自由定義規(guī)則隅津,而且诬垂,變更被映射的IP或者機器故障時很麻煩,還存在DNS生效延遲的問題伦仍。
5. DNS/GSLB負載均衡
我們常用的CDN(Content Delivery Network结窘,內(nèi)容分發(fā)網(wǎng)絡(luò))實現(xiàn)方式,其實就是在同一個域名映射為多IP的基礎(chǔ)上更進一步充蓝,通過GSLB(Global Server Load Balance隧枫,全局負載均衡)按照指定規(guī)則映射域名的IP。一般情況下都是按照地理位置谓苟,將離用戶近的IP返回給用戶官脓,減少網(wǎng)絡(luò)傳輸中的路由節(jié)點之間的跳躍消耗。
圖中的“向上尋找”涝焙,實際過程是LDNS(Local DNS)先向根域名服務(wù)(Root Name Server)獲取到頂級根的Name Server(例如.com的)卑笨,然后得到指定域名的授權(quán)DNS,然后再獲得實際服務(wù)器IP纱皆。
CDN在Web系統(tǒng)中湾趾,一般情況下是用來解決大小較大的靜態(tài)資源(html/Js/Css/圖片等)的加載問題,讓這些比較依賴網(wǎng)絡(luò)下載的內(nèi)容派草,盡可能離用戶更近搀缠,提升用戶體驗。
例如近迁,我訪問了一張imgcache.gtimg.cn上的圖片(騰訊的自建CDN艺普,不使用qq.com域名的原因是防止http請求的時候,帶上了多余的cookie信息)鉴竭,我獲得的IP是183.60.217.90歧譬。
這種方式,和前面的DNS負載均衡一樣搏存,不僅性能極佳瑰步,而且支持配置多種策略。但是璧眠,搭建和維護成本非常高缩焦《谅玻互聯(lián)網(wǎng)一線公司,會自建CDN服務(wù)袁滥,中小型公司一般使用第三方提供的CDN盖桥。
Web系統(tǒng)的緩存機制的建立和優(yōu)化
剛剛我們講完了Web系統(tǒng)的外部網(wǎng)絡(luò)環(huán)境,現(xiàn)在我們開始關(guān)注我們Web系統(tǒng)自身的性能問題题翻。我們的Web站點隨著訪問量的上升揩徊,會遇到很多的挑戰(zhàn),解決這些問題不僅僅是擴容機器這么簡單嵌赠,建立和使用合適的緩存機制才是根本塑荒。
最開始,我們的Web系統(tǒng)架構(gòu)可能是這樣的猾普,每個環(huán)節(jié)袜炕,都可能只有1臺機器。
我們從最根本的數(shù)據(jù)存儲開始看哈初家。
一偎窘、 MySQL數(shù)據(jù)庫內(nèi)部緩存使用
MySQL的緩存機制,就從先從MySQL內(nèi)部開始溜在,下面的內(nèi)容將以最常見的InnoDB存儲引擎為主陌知。
1. 建立恰當(dāng)?shù)乃饕?/p>
最簡單的是建立索引,索引在表數(shù)據(jù)比較大的時候掖肋,起到快速檢索數(shù)據(jù)的作用仆葡,但是成本也是有的。首先志笼,占用了一定的磁盤空間沿盅,其中組合索引最突出,使用需要謹(jǐn)慎纫溃,它產(chǎn)生的索引甚至?xí)仍磾?shù)據(jù)更大腰涧。其次,建立索引之后的數(shù)據(jù)insert/update/delete等操作紊浩,因為需要更新原來的索引窖铡,耗時會增加。當(dāng)然坊谁,實際上我們的系統(tǒng)從總體來說费彼,是以select查詢操作居多,因此口芍,索引的使用仍然對系統(tǒng)性能有大幅提升的作用箍铲。
2. 數(shù)據(jù)庫連接線程池緩存
如果,每一個數(shù)據(jù)庫操作請求都需要創(chuàng)建和銷毀連接的話鬓椭,對數(shù)據(jù)庫來說虹钮,無疑也是一種巨大的開銷聋庵。為了減少這類型的開銷,可以在MySQL中配置thread_cache_size來表示保留多少線程用于復(fù)用芙粱。線程不夠的時候,再創(chuàng)建氧映,空閑過多的時候春畔,則銷毀。
其實岛都,還有更為激進一點的做法律姨,使用pconnect(數(shù)據(jù)庫長連接),線程一旦創(chuàng)建在很長時間內(nèi)都保持著臼疫。但是择份,在訪問量比較大,機器比較多的情況下烫堤,這種用法很可能會導(dǎo)致“數(shù)據(jù)庫連接數(shù)耗盡”荣赶,因為建立連接并不回收,最終達到數(shù)據(jù)庫的max_connections(最大連接數(shù))鸽斟。因此拔创,長連接的用法通常需要在CGI和MySQL之間實現(xiàn)一個“連接池”服務(wù),控制CGI機器“盲目”創(chuàng)建連接數(shù)富蓄。
建立數(shù)據(jù)庫連接池服務(wù)剩燥,有很多實現(xiàn)的方式,PHP的話立倍,我推薦使用swoole(PHP的一個網(wǎng)絡(luò)通訊拓展)來實現(xiàn)灭红。
3. Innodb緩存設(shè)置(innodb_buffer_pool_size)
innodb_buffer_pool_size這是個用來保存索引和數(shù)據(jù)的內(nèi)存緩存區(qū),如果機器是MySQL獨占的機器口注,一般推薦為機器物理內(nèi)存的80%变擒。在取表數(shù)據(jù)的場景中,它可以減少磁盤IO疆导。一般來說赁项,這個值設(shè)置越大,cache命中率會越高澈段。
4. 分庫/分表/分區(qū)悠菜。
MySQL數(shù)據(jù)庫表一般承受數(shù)據(jù)量在百萬級別,再往上增長败富,各項性能將會出現(xiàn)大幅度下降悔醋,因此,當(dāng)我們預(yù)見數(shù)據(jù)量會超過這個量級的時候兽叮,建議進行分庫/分表/分區(qū)等操作芬骄。最好的做法猾愿,是服務(wù)在搭建之初就設(shè)計為分庫分表的存儲模式,從根本上杜絕中后期的風(fēng)險账阻。不過蒂秘,會犧牲一些便利性,例如列表式的查詢淘太,同時姻僧,也增加了維護的復(fù)雜度。不過蒲牧,到了數(shù)據(jù)量千萬級別或者以上的時候撇贺,我們會發(fā)現(xiàn),它們都是值得的冰抢。
二松嘶、 MySQL數(shù)據(jù)庫多臺服務(wù)搭建
1臺MySQL機器,實際上是高風(fēng)險的單點挎扰,因為如果它掛了翠订,我們Web服務(wù)就不可用了。而且鼓鲁,隨著Web系統(tǒng)訪問量繼續(xù)增加蕴轨,終于有一天,我們發(fā)現(xiàn)1臺MySQL服務(wù)器無法支撐下去骇吭,我們開始需要使用更多的MySQL機器橙弱。當(dāng)引入多臺MySQL機器的時候,很多新的問題又將產(chǎn)生燥狰。
1. 建立MySQL主從棘脐,從庫作為備份
這種做法純粹為了解決“單點故障”的問題,在主庫出故障的時候龙致,切換到從庫蛀缝。不過,這種做法實際上有點浪費資源目代,因為從庫實際上被閑著了屈梁。
2. MySQL讀寫分離,主庫寫榛了,從庫讀在讶。
兩臺數(shù)據(jù)庫做讀寫分離,主庫負責(zé)寫入類的操作霜大,從庫負責(zé)讀的操作构哺。并且,如果主庫發(fā)生故障战坤,仍然不影響讀的操作曙强,同時也可以將全部讀寫都臨時切換到從庫中(需要注意流量残拐,可能會因為流量過大,把從庫也拖垮)碟嘴。
3. 主主互備溪食。
兩臺MySQL之間互為彼此的從庫,同時又是主庫娜扇。這種方案眠菇,既做到了訪問量的壓力分流,同時也解決了“單點故障”問題袱衷。任何一臺故障,都還有另外一套可供使用的服務(wù)笑窜。
不過致燥,這種方案,只能用在兩臺機器的場景排截。如果業(yè)務(wù)拓展還是很快的話嫌蚤,可以選擇將業(yè)務(wù)分離,建立多個主主互備断傲。
三脱吱、 MySQL數(shù)據(jù)庫機器之間的數(shù)據(jù)同步
每當(dāng)我們解決一個問題,新的問題必然誕生在舊的解決方案上认罩。當(dāng)我們有多臺MySQL箱蝠,在業(yè)務(wù)高峰期,很可能出現(xiàn)兩個庫之間的數(shù)據(jù)有延遲的場景垦垂。并且宦搬,網(wǎng)絡(luò)和機器負載等,也會影響數(shù)據(jù)同步的延遲劫拗。我們曾經(jīng)遇到過间校,在日訪問量接近1億的特殊場景下,出現(xiàn)页慷,從庫數(shù)據(jù)需要很多天才能同步追上主庫的數(shù)據(jù)憔足。這種場景下,從庫基本失去效用了酒繁。
于是滓彰,解決同步問題,就是我們下一步需要關(guān)注的點欲逃。
1. MySQL自帶多線程同步
MySQL5.6開始支持主庫和從庫數(shù)據(jù)同步找蜜,走多線程。但是稳析,限制也是比較明顯的洗做,只能以庫為單位弓叛。MySQL數(shù)據(jù)同步是通過binlog日志,主庫寫入到binlog日志的操作诚纸,是具有順序的撰筷,尤其當(dāng)SQL操作中含有對于表結(jié)構(gòu)的修改等操作,對于后續(xù)的SQL語句操作是有影響的畦徘。因此毕籽,從庫同步數(shù)據(jù),必須走單進程井辆。
2. 自己實現(xiàn)解析binlog关筒,多線程寫入。
以數(shù)據(jù)庫的表為單位杯缺,解析binlog多張表同時做數(shù)據(jù)同步蒸播。這樣做的話,的確能夠加快數(shù)據(jù)同步的效率萍肆,但是袍榆,如果表和表之間存在結(jié)構(gòu)關(guān)系或者數(shù)據(jù)依賴的話,則同樣存在寫入順序的問題塘揣。這種方式包雀,可用于一些比較穩(wěn)定并且相對獨立的數(shù)據(jù)表。
國內(nèi)一線互聯(lián)網(wǎng)公司亲铡,大部分都是通過這種方式才写,來加快數(shù)據(jù)同步效率。還有更為激進的做法奴愉,是直接解析binlog琅摩,忽略以表為單位,直接寫入锭硼。但是這種做法房资,實現(xiàn)復(fù)雜,使用范圍就更受到限制檀头,只能用于一些場景特殊的數(shù)據(jù)庫中(沒有表結(jié)構(gòu)變更轰异,表和表之間沒有數(shù)據(jù)依賴等特殊表)。
四暑始、 在Web服務(wù)器和數(shù)據(jù)庫之間建立緩存
實際上搭独,解決大訪問量的問題,不能僅僅著眼于數(shù)據(jù)庫層面廊镜。根據(jù)“二八定律”牙肝,80%的請求只關(guān)注在20%的熱點數(shù)據(jù)上。因此,我們應(yīng)該建立Web服務(wù)器和數(shù)據(jù)庫之間的緩存機制配椭。這種機制虫溜,可以用磁盤作為緩存,也可以用內(nèi)存緩存的方式股缸。通過它們衡楞,將大部分的熱點數(shù)據(jù)查詢,阻擋在數(shù)據(jù)庫之前敦姻。
1. 頁面靜態(tài)化
用戶訪問網(wǎng)站的某個頁面瘾境,頁面上的大部分內(nèi)容在很長一段時間內(nèi),可能都是沒有變化的镰惦。例如一篇新聞報道迷守,一旦發(fā)布幾乎是不會修改內(nèi)容的。這樣的話旺入,通過CGI生成的靜態(tài)html頁面緩存到Web服務(wù)器的磁盤本地盒犹。除了第一次,是通過動態(tài)CGI查詢數(shù)據(jù)庫獲取之外眨业,之后都直接將本地磁盤文件返回給用戶。
在Web系統(tǒng)規(guī)模比較小的時候沮协,這種做法看似完美龄捡。但是,一旦Web系統(tǒng)規(guī)模變大慷暂,例如當(dāng)我有100臺的Web服務(wù)器的時候聘殖。那樣這些磁盤文件,將會有100份行瑞,這個是資源浪費奸腺,也不好維護。這個時候有人會想血久,可以集中一臺服務(wù)器存起來突照,呵呵,不如看看下面一種緩存方式吧氧吐,它就是這樣做的讹蘑。
2. 單臺內(nèi)存緩存
通過頁面靜態(tài)化的例子中,我們可以知道將“緩存”搭建在Web機器本機是不好維護的筑舅,會帶來更多問題(實際上座慰,通過PHP的apc拓展,可通過Key/value操作Web服務(wù)器的本機內(nèi)存)翠拣。因此版仔,我們選擇搭建的內(nèi)存緩存服務(wù),也必須是一個獨立的服務(wù)。
內(nèi)存緩存的選擇蛮粮,主要有redis/memcache益缎。從性能上說,兩者差別不大蝉揍,從功能豐富程度上說链峭,Redis更勝一籌。
3. 內(nèi)存緩存集群
當(dāng)我們搭建單臺內(nèi)存緩存完畢又沾,我們又會面臨單點故障的問題弊仪,因此,我們必須將它變成一個集群杖刷。簡單的做法励饵,是給他增加一個slave作為備份機器。但是滑燃,如果請求量真的很多役听,我們發(fā)現(xiàn)cache命中率不高,需要更多的機器內(nèi)存呢表窘?因此典予,我們更建議將它配置成一個集群。例如乐严,類似redis cluster瘤袖。
Redis cluster集群內(nèi)的Redis互為多組主從,同時每個節(jié)點都可以接受請求昂验,在拓展集群的時候比較方便捂敌。客戶端可以向任意一個節(jié)點發(fā)送請求既琴,如果是它的“負責(zé)”的內(nèi)容占婉,則直接返回內(nèi)容。否則甫恩,查找實際負責(zé)Redis節(jié)點逆济,然后將地址告知客戶端,客戶端重新請求磺箕。
對于使用緩存服務(wù)的客戶端來說纹腌,這一切是透明的。
內(nèi)存緩存服務(wù)在切換的時候滞磺,是有一定風(fēng)險的升薯。從A集群切換到B集群的過程中,必須保證B集群提前做好“預(yù)熱”(B集群的內(nèi)存中的熱點數(shù)據(jù)击困,應(yīng)該盡量與A集群相同涎劈,否則广凸,切換的一瞬間大量請求內(nèi)容,在B集群的內(nèi)存緩存中查找不到蛛枚,流量直接沖擊后端的數(shù)據(jù)庫服務(wù)谅海,很可能導(dǎo)致數(shù)據(jù)庫宕機)。
4. 減少數(shù)據(jù)庫“寫”
上面的機制蹦浦,都實現(xiàn)減少數(shù)據(jù)庫的“讀”的操作扭吁,但是,寫的操作也是一個大的壓力盲镶。寫的操作侥袜,雖然無法減少,但是可以通過合并請求溉贿,來起到減輕壓力的效果枫吧。這個時候,我們就需要在內(nèi)存緩存集群和數(shù)據(jù)庫集群之間宇色,建立一個修改同步機制九杂。
先將修改請求生效在cache中,讓外界查詢顯示正常宣蠕,然后將這些sql修改放入到一個隊列中存儲起來例隆,隊列滿或者每隔一段時間,合并為一個請求到數(shù)據(jù)庫中更新數(shù)據(jù)庫抢蚀。
除了上述通過改變系統(tǒng)架構(gòu)的方式提升寫的性能外裳擎,MySQL本身也可以通過配置參數(shù)innodb_flush_log_at_trx_commit來調(diào)整寫入磁盤的策略。如果機器成本允許思币,從硬件層面解決問題,可以選擇老一點的RAID(Redundant Arrays of independent Disks羡微,磁盤列陣)或者比較新的SSD(Solid State Drives谷饿,固態(tài)硬盤)。
5. NoSQL存儲
不管數(shù)據(jù)庫的讀還是寫妈倔,當(dāng)流量再進一步上漲博投,終會達到“人力有窮時”的場景。繼續(xù)加機器的成本比較高盯蝴,并且不一定可以真正解決問題的時候毅哗。這個時候,部分核心數(shù)據(jù)捧挺,就可以考慮使用NoSQL的數(shù)據(jù)庫虑绵。NoSQL存儲,大部分都是采用key-value的方式闽烙,這里比較推薦使用上面介紹過Redis翅睛,Redis本身是一個內(nèi)存cache声搁,同時也可以當(dāng)做一個存儲來使用,讓它直接將數(shù)據(jù)落地到磁盤捕发。
這樣的話疏旨,我們就將數(shù)據(jù)庫中某些被頻繁讀寫的數(shù)據(jù),分離出來扎酷,放在我們新搭建的Redis存儲集群中檐涝,又進一步減輕原來MySQL數(shù)據(jù)庫的壓力,同時因為Redis本身是個內(nèi)存級別的Cache法挨,讀寫的性能都會大幅度提升谁榜。
國內(nèi)一線互聯(lián)網(wǎng)公司,架構(gòu)上采用的解決方案很多是類似于上述方案坷剧,不過惰爬,使用的cache服務(wù)卻不一定是Redis,他們會有更豐富的其他選擇惫企,甚至根據(jù)自身業(yè)務(wù)特點開發(fā)出自己的NoSQL服務(wù)撕瞧。
6. 空節(jié)點查詢問題
當(dāng)我們搭建完前面所說的全部服務(wù),認(rèn)為Web系統(tǒng)已經(jīng)很強的時候狞尔。我們還是那句話丛版,新的問題還是會來的∑颍空節(jié)點查詢页畦,是指那些數(shù)據(jù)庫中根本不存在的數(shù)據(jù)請求。例如研儒,我請求查詢一個不存在人員信息豫缨,系統(tǒng)會從各級緩存逐級查找,最后查到到數(shù)據(jù)庫本身端朵,然后才得出查找不到的結(jié)論好芭,返回給前端。因為各級cache對它無效冲呢,這個請求是非常消耗系統(tǒng)資源的舍败,而如果大量的空節(jié)點查詢,是可以沖擊到系統(tǒng)服務(wù)的敬拓。
在我曾經(jīng)的工作經(jīng)歷中邻薯,曾深受其害。因此乘凸,為了維護Web系統(tǒng)的穩(wěn)定性厕诡,設(shè)計適當(dāng)?shù)目展?jié)點過濾機制,非常有必要。
我們當(dāng)時采用的方式,就是設(shè)計一張簡單的記錄映射表。將存在的記錄存儲起來舆声,放入到一臺內(nèi)存cache中醒第,這樣的話渔嚷,如果還有空節(jié)點查詢,則在緩存這一層就被阻擋了稠曼。
異地部署(地理分布式)
完成了上述架構(gòu)建設(shè)之后形病,我們的系統(tǒng)是否就已經(jīng)足夠強大了呢?答案當(dāng)然是否定的哈霞幅,優(yōu)化是無極限的漠吻。Web系統(tǒng)雖然表面上看,似乎比較強大了司恳,但是給予用戶的體驗卻不一定是最好的途乃。因為東北的同學(xué),訪問深圳的一個網(wǎng)站服務(wù)扔傅,他還是會感到一些網(wǎng)絡(luò)距離上的慢耍共。這個時候,我們就需要做異地部署猎塞,讓W(xué)eb系統(tǒng)離用戶更近试读。
一、 核心集中與節(jié)點分散
有玩過大型網(wǎng)游的同學(xué)都會知道荠耽,網(wǎng)游是有很多個區(qū)的钩骇,一般都是按照地域來分,例如廣東專區(qū)铝量,北京專區(qū)倘屹。如果一個在廣東的玩家,去北京專區(qū)玩慢叨,那么他會感覺明顯比在廣東專區(qū)卡纽匙。實際上,這些大區(qū)的名稱就已經(jīng)說明了插爹,它的服務(wù)器所在地,所以请梢,廣東的玩家去連接地處北京的服務(wù)器赠尾,網(wǎng)絡(luò)當(dāng)然會比較慢。
當(dāng)一個系統(tǒng)和服務(wù)足夠大的時候毅弧,就必須開始考慮異地部署的問題了气嫁。讓你的服務(wù),盡可能離用戶更近够坐。我們前面已經(jīng)提到了Web的靜態(tài)資源寸宵,可以存放在CDN上崖面,然后通過DNS/GSLB的方式,讓靜態(tài)資源的分散“全國各地”梯影。但是巫员,CDN只解決的靜態(tài)資源的問題,沒有解決后端龐大的系統(tǒng)服務(wù)還只集中在某個固定城市的問題甲棍。
這個時候简识,異地部署就開始了。異地部署一般遵循:核心集中感猛,節(jié)點分散七扰。
核心集中:實際部署過程中,總有一部分的數(shù)據(jù)和服務(wù)存在不可部署多套陪白,或者部署多套成本巨大颈走。而對于這些服務(wù)和數(shù)據(jù),就仍然維持一套咱士,而部署地點選擇一個地域比較中心的地方立由,通過網(wǎng)絡(luò)內(nèi)部專線來和各個節(jié)點通訊。
節(jié)點分散:將一些服務(wù)部署為多套司致,分布在各個城市節(jié)點拆吆,讓用戶請求盡可能選擇近的節(jié)點訪問服務(wù)。
例如脂矫,我們選擇在上海部署為核心節(jié)點枣耀,北京,深圳庭再,武漢捞奕,上海為分散節(jié)點(上海自己本身也是一個分散節(jié)點)。我們的服務(wù)架構(gòu)如圖:
需要補充一下的是拄轻,上圖中上海節(jié)點和核心節(jié)點是同處于一個機房的颅围,其他分散節(jié)點各自獨立機房。
國內(nèi)有很多大型網(wǎng)游恨搓,都是大致遵循上述架構(gòu)院促。它們會把數(shù)據(jù)量不大的用戶核心賬號等放在核心節(jié)點,而大部分的網(wǎng)游數(shù)據(jù)斧抱,例如裝備常拓、任務(wù)等數(shù)據(jù)和服務(wù)放在地區(qū)節(jié)點里。當(dāng)然辉浦,核心節(jié)點和地域節(jié)點之間弄抬,也有緩存機制。
二宪郊、 節(jié)點容災(zāi)和過載保護
節(jié)點容災(zāi)是指掂恕,某個節(jié)點如果發(fā)生故障時拖陆,我們需要建立一個機制去保證服務(wù)仍然可用。毫無疑問懊亡,這里比較常見的容災(zāi)方式依啰,是切換到附近城市節(jié)點。假如系統(tǒng)的天津節(jié)點發(fā)生故障斋配,那么我們就將網(wǎng)絡(luò)流量切換到附近的北京節(jié)點上孔飒。考慮到負載均衡艰争,可能需要同時將流量切換到附近的幾個地域節(jié)點坏瞄。另一方面,核心節(jié)點自身也是需要自己做好容災(zāi)和備份的甩卓,核心節(jié)點一旦故障鸠匀,就會影響全國服務(wù)。
過載保護逾柿,指的是一個節(jié)點已經(jīng)達到最大容量缀棍,無法繼續(xù)接接受更多請求了,系統(tǒng)必須有一個保護的機制机错。一個服務(wù)已經(jīng)滿負載爬范,還繼續(xù)接受新的請求,結(jié)果很可能就是宕機弱匪,影響整個節(jié)點的服務(wù)青瀑,為了至少保障大部分用戶的正常使用,過載保護是必要的萧诫。
解決過載保護斥难,一般2個方向:
拒絕服務(wù),檢測到滿負載之后帘饶,就不再接受新的連接請求哑诊。例如網(wǎng)游登入中的排隊。
分流到其他節(jié)點及刻。這種的話镀裤,系統(tǒng)實現(xiàn)更為復(fù)雜,又涉及到負載均衡的問題缴饭。
小結(jié)
Web系統(tǒng)會隨著訪問規(guī)模的增長暑劝,漸漸地從1臺服務(wù)器可以滿足需求,一直成長為“龐然大物”的大集群茴扁。而這個Web系統(tǒng)變大的過程铃岔,實際上就是我們解決問題的過程汪疮。在不同的階段峭火,解決不同的問題毁习,而新的問題又誕生在舊的解決方案之上。
系統(tǒng)的優(yōu)化是沒有極限的卖丸,軟件和系統(tǒng)架構(gòu)也一直在快速發(fā)展纺且,新的方案解決了老的問題,同時也帶來新的挑戰(zhàn)稍浆。