歐陽(yáng)習(xí)彪,徐寶林
(廣東白云學(xué)院大數(shù)據(jù)與計(jì)算機(jī)學(xué)院,廣州 510450)
高并發(fā)系統(tǒng)是當(dāng)今互聯(lián)網(wǎng)時(shí)代的關(guān)鍵技術(shù)之一。隨著互聯(lián)網(wǎng)用戶數(shù)量的不斷增加,社交、媒體以及電商、游戲等Web 網(wǎng)站用戶數(shù)量越來(lái)越大,并發(fā)流量也越來(lái)越高,這對(duì)于傳統(tǒng)Web系統(tǒng)架構(gòu)設(shè)計(jì)提出新的挑戰(zhàn),如何構(gòu)建高效、穩(wěn)定、可擴(kuò)展的系統(tǒng)成為了互聯(lián)網(wǎng)企業(yè)必須面對(duì)的問(wèn)題[1]。高并發(fā)系統(tǒng)架構(gòu)作為解決這一問(wèn)題的核心技術(shù),已經(jīng)成為了各大互聯(lián)網(wǎng)企業(yè)競(jìng)爭(zhēng)的重要因素。本文將從架構(gòu)設(shè)計(jì)和技術(shù)選型及實(shí)現(xiàn)等方面,對(duì)高并發(fā)系統(tǒng)架構(gòu)進(jìn)行深入研究。探討如何通過(guò)分布式架構(gòu)、緩存技術(shù)、負(fù)載均衡等手段來(lái)提高系統(tǒng)的性能和可用性,從而為用戶提供更好的服務(wù)體驗(yàn)。
對(duì)傳統(tǒng)虛擬機(jī)技術(shù)下LAMP架構(gòu)部署的Web應(yīng)用資源消耗大、部署速度較慢等問(wèn)題,使用Docker 可更快地打包、測(cè)試以及部署應(yīng)用程序,過(guò)去需要用數(shù)天乃至數(shù)周的任務(wù),在Docker 容器的處理下,只需要數(shù)秒就能完成,提供持續(xù)集成和持續(xù)部署的服務(wù)。同時(shí)Docker 容器包含了運(yùn)行環(huán)境和可執(zhí)行程序,可以跨平臺(tái)和主機(jī)使用,也避免了開(kāi)發(fā)環(huán)境、測(cè)試環(huán)境、生成環(huán)境不一致的問(wèn)題[2]。在本系統(tǒng)中采用dockercompose 編排工具來(lái)創(chuàng)建容器和鏡像,dockercompose.yml 配置內(nèi)容如圖1 所示,通過(guò)dockercompose 命令啟動(dòng)容器,docker images 可查看到系統(tǒng)共創(chuàng)建了php+nginx+mysql+redis 四個(gè)鏡像以及docker ps-a命令可看到共創(chuàng)建了php+nginx+mysql+redis四個(gè)容器,如圖2所示。
圖2 創(chuàng)建的容器及鏡像
面向高并發(fā)的Web 系統(tǒng)架構(gòu)設(shè)計(jì)的核心思想是降低服務(wù)器端對(duì)資源調(diào)度和使用的程度,除了在程序設(shè)計(jì)應(yīng)用高效的算法之外,在系統(tǒng)架構(gòu)上可以采取分布式架構(gòu)、緩存技術(shù)、負(fù)載均衡等技術(shù)來(lái)降低服務(wù)器端的數(shù)據(jù)處理性能開(kāi)銷(xiāo)[3]。系統(tǒng)架構(gòu)設(shè)計(jì)如圖3所示。
圖3 系統(tǒng)架構(gòu)
CDN 加速的原理是通過(guò)在現(xiàn)有的網(wǎng)絡(luò)中增加一層網(wǎng)絡(luò)架構(gòu),將目標(biāo)網(wǎng)站的內(nèi)容發(fā)布到最接近用戶的網(wǎng)絡(luò)“邊緣”,使用戶可以就近取得所需的內(nèi)容,提高用戶訪問(wèn)網(wǎng)站的響應(yīng)速度。CDN 主要是用來(lái)緩存網(wǎng)站中的靜態(tài)數(shù)據(jù),如:CSS、JS、圖片和靜態(tài)頁(yè)面等數(shù)據(jù)。用戶發(fā)送請(qǐng)求到后端服務(wù)器,處理完動(dòng)態(tài)內(nèi)容后,直接從CDN中獲取靜態(tài)數(shù)據(jù),從而加快響應(yīng)時(shí)間[4]。
在高并發(fā)系統(tǒng)中,需要保證系統(tǒng)的高可用性和負(fù)載均衡,而Keepalived LVS(linux virtual server)是一種常見(jiàn)的解決方案。Keepalived LVS通過(guò)將請(qǐng)求分發(fā)到多個(gè)服務(wù)器上,來(lái)實(shí)現(xiàn)負(fù)載均衡和高可用性。它使用IP 負(fù)載均衡技術(shù)將網(wǎng)絡(luò)流量分發(fā)到多個(gè)服務(wù)器,并使用虛擬IP 地址來(lái)屏蔽后端服務(wù)器的IP 地址。當(dāng)一個(gè)服務(wù)器故障時(shí),Keepalived LVS 會(huì)將請(qǐng)求重新路由到其他可用服務(wù)器上,從而保證系統(tǒng)的高可用性。
2.3.1 動(dòng)靜分離
通過(guò)中間件將動(dòng)態(tài)請(qǐng)求和靜態(tài)請(qǐng)求分離,可以減少不必要的請(qǐng)求消耗,同時(shí)能減少請(qǐng)求的延時(shí)。動(dòng)靜分離后,即使動(dòng)態(tài)服務(wù)不可用,靜態(tài)資源也不會(huì)受到影響。
2.3.2 負(fù)載均衡
隨著網(wǎng)站用戶量不斷增大,同一時(shí)間請(qǐng)求數(shù)不斷提高,單臺(tái)服務(wù)器已經(jīng)不能滿足需要,此時(shí)需要進(jìn)行服務(wù)器擴(kuò)容,將客戶端發(fā)過(guò)來(lái)的請(qǐng)求分?jǐn)偟狡渌?wù)器上,減少每臺(tái)服務(wù)器的壓力,進(jìn)而提高系統(tǒng)的吞吐率;另外如果其中某一臺(tái)服務(wù)器宕機(jī),其他服務(wù)器還可以正常提供服務(wù),以此來(lái)提高系統(tǒng)的可伸縮性與可靠性。常見(jiàn)的負(fù)載均衡算法有輪詢(xún)、加權(quán)輪詢(xún)和hash。在本游戲系統(tǒng)中由于三臺(tái)服務(wù)器配置一樣,故選用加權(quán)輪詢(xún)的策略,nginx 負(fù)載均衡主要配置如圖4所示。
圖4 nginx負(fù)載均衡機(jī)制示意圖
2.4.1 分布式數(shù)據(jù)庫(kù)
高并發(fā)系統(tǒng)中,隨著業(yè)務(wù)的發(fā)展,系統(tǒng)用戶數(shù)越來(lái)越多,單表數(shù)量達(dá)到一定量的時(shí)候,可能會(huì)導(dǎo)致表中索引失效,查詢(xún)速度變得非常慢,同時(shí)現(xiàn)有的單臺(tái)數(shù)據(jù)庫(kù)服務(wù)器滿足不了業(yè)務(wù)需求,需要進(jìn)行擴(kuò)容,本系統(tǒng)主要采用數(shù)據(jù)庫(kù)分布式部署、負(fù)載均衡、分庫(kù)分表、讀寫(xiě)分離的技術(shù)來(lái)滿足高并發(fā)場(chǎng)景下數(shù)據(jù)庫(kù)服務(wù)器的高性能、高可用特性[5]。
(1)讀寫(xiě)分離:由于業(yè)務(wù)中大多數(shù)是處理讀的操作,數(shù)據(jù)庫(kù)的壓力主要是由這些讀的請(qǐng)求造成的,通過(guò)數(shù)據(jù)庫(kù)讀寫(xiě)分離,能有效減少數(shù)據(jù)庫(kù)的壓力,提高查詢(xún)響應(yīng)速度。
(2)主從復(fù)制:對(duì)系統(tǒng)進(jìn)行讀寫(xiě)分離后,主服務(wù)器負(fù)責(zé)寫(xiě)入操作,從服務(wù)器負(fù)責(zé)讀操作,由于讀和寫(xiě)操作不是同一個(gè)表,會(huì)導(dǎo)致數(shù)據(jù)不一致的問(wèn)題。因此,需要通過(guò)主從復(fù)制的方式來(lái)同步數(shù)據(jù),保證主從服務(wù)器數(shù)據(jù)的一致。
(3)數(shù)據(jù)庫(kù)負(fù)載均衡:在主從部署的數(shù)據(jù)庫(kù)集群系統(tǒng)中,從服務(wù)器通常有幾臺(tái),為了分?jǐn)傁到y(tǒng)請(qǐng)求壓力,最大化利用每臺(tái)從服務(wù)器,本系統(tǒng)中采用Haproxy+Keepalived 負(fù)載均衡技術(shù),通過(guò)Haproxy 實(shí)現(xiàn)負(fù)載均衡,Keepalived 確保即使主服務(wù)器宕機(jī),從服務(wù)器仍舊可以作為主服務(wù)器使用,保證系統(tǒng)的高可用性。
(4)分庫(kù)分表:在高并發(fā)數(shù)據(jù)量大的系統(tǒng)中,頻繁的IO 操作成了數(shù)據(jù)庫(kù)的性能瓶頸,最終都會(huì)導(dǎo)致數(shù)據(jù)庫(kù)的活躍連接數(shù)增加,進(jìn)而逼近甚至達(dá)到數(shù)據(jù)庫(kù)可承載活躍連接數(shù)的閾值,使得可用數(shù)據(jù)庫(kù)連接少甚至無(wú)連接可用。通過(guò)分庫(kù)分表,將數(shù)據(jù)分散存儲(chǔ),使得單一數(shù)據(jù)庫(kù)/表的數(shù)據(jù)量變小來(lái)緩解單一數(shù)據(jù)庫(kù)的性能問(wèn)題。在本游戲系統(tǒng)中,用戶千萬(wàn)級(jí),游戲記錄表數(shù)據(jù)每日新增量非常大,單表已不能滿足需要,故采用范圍分表的方式,共分為20 個(gè)子表,每個(gè)子表存放500000 條記錄,根據(jù)用戶ID 對(duì)單表最大記錄數(shù)500000 取商,再加1,以此確定當(dāng)前用戶游戲數(shù)據(jù)對(duì)應(yīng)保存的子表序號(hào),具體實(shí)現(xiàn)代碼如下:
2.4.2 數(shù)據(jù)緩存redis
當(dāng)表的記錄變得非常龐大時(shí),索引失效,查詢(xún)速度將變得非常慢,影響網(wǎng)站的性能,這種情況下可以將數(shù)據(jù)緩存起來(lái),每次訪問(wèn)數(shù)據(jù)的時(shí)候先從緩存中讀取,如果緩存中沒(méi)有需要的數(shù)據(jù),才去數(shù)據(jù)庫(kù)中查找。這樣可以極大降低數(shù)據(jù)庫(kù)的負(fù)載壓力,也有效提高了獲取數(shù)據(jù)的速度。常用的緩存技術(shù)有redis 和memcache,由于redis 擁有豐富的數(shù)據(jù)類(lèi)型,支持?jǐn)?shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保持在磁盤(pán)中,重啟的時(shí)候可以再次加載進(jìn)行使用。系統(tǒng)中用redis作為緩存數(shù)據(jù)庫(kù),該游戲系統(tǒng)中用戶量大、并發(fā)高,同一時(shí)刻需要更新玩家數(shù)據(jù)的量非常大,如果直接操作數(shù)據(jù)庫(kù)將導(dǎo)致數(shù)據(jù)庫(kù)無(wú)法承受壓力而崩潰,此時(shí)可以選用redis 的hash 數(shù)據(jù)類(lèi)型,以用戶id 作為鍵,玩家游戲數(shù)據(jù)作為值,同時(shí)將有數(shù)據(jù)變換的玩家id存放到redis集合中。然后通過(guò)定時(shí)任務(wù),從redis 集合中獲取所有玩家有數(shù)據(jù)變化的玩家id,并依次取出數(shù)據(jù)內(nèi)容插入數(shù)據(jù)庫(kù)中,具體實(shí)現(xiàn)代碼如下:
由于redis 是純內(nèi)存操作,內(nèi)存空間有限,在高并發(fā)系統(tǒng)中一臺(tái)redis 服務(wù)器并不能滿足系統(tǒng)高可用的要求,本游戲系統(tǒng)中采用一主二從的架構(gòu),通過(guò)主從復(fù)制實(shí)現(xiàn)了數(shù)據(jù)的熱備份,當(dāng)主服務(wù)器宕機(jī)時(shí),從服務(wù)器可以充當(dāng)主服務(wù)器進(jìn)行使用,同時(shí)在主從復(fù)制的基礎(chǔ)上,配合讀寫(xiě)分離,可以由主節(jié)點(diǎn)提供寫(xiě)服務(wù),由從節(jié)點(diǎn)提供讀服務(wù),分擔(dān)服務(wù)器負(fù)載;尤其是在寫(xiě)多讀少的情況下,通過(guò)多個(gè)從節(jié)點(diǎn)分擔(dān)讀負(fù)載,可以大大提高redis服務(wù)器的并發(fā)量。
對(duì)于大流量、高并發(fā)系統(tǒng),任何一個(gè)環(huán)節(jié)到達(dá)性能瓶頸都可能導(dǎo)致系統(tǒng)宕機(jī)崩潰,進(jìn)行在進(jìn)行系統(tǒng)架構(gòu)設(shè)計(jì)時(shí),每一層都需要考慮系統(tǒng)的可用性、擴(kuò)展性、安全性,等等。本文從接入層、應(yīng)用層、數(shù)據(jù)存儲(chǔ)層三個(gè)方面進(jìn)行了探討,利用DNS 加速、負(fù)載均衡、redis 主從、MySQL 主從復(fù)制、MySQL 讀寫(xiě)分離等技術(shù)實(shí)現(xiàn)了系統(tǒng)高并發(fā)、高性能、高可用的特性。