焦 宇,李 民,王 歡,余開朝
(昆明理工大學(xué)機電工程學(xué)院,云南昆明 650500)
隨著互聯(lián)網(wǎng)技術(shù)的迅速發(fā)展,人們與網(wǎng)絡(luò)的關(guān)系日益密切,互聯(lián)網(wǎng)帶給人們一種全新的生活方式,如網(wǎng)上購物、滴滴打車、學(xué)習(xí)網(wǎng)課等都為人們的生活帶來了全新的體驗,而近些年的網(wǎng)上購物更是發(fā)展得如火如荼[1]。網(wǎng)購系統(tǒng)的出現(xiàn)極大地改善了人們的購物體驗,雖然不能完全替代線下購物,但已逐漸成為人們主流的購物方式。由此,對網(wǎng)購系統(tǒng)設(shè)計要求隨之提高,如遇到某些購物活動,大量客戶進(jìn)入系統(tǒng)進(jìn)行購物,傳統(tǒng)的單機部署式網(wǎng)購系統(tǒng)已經(jīng)無法承受如此高的并發(fā)量,會發(fā)生提取數(shù)據(jù)緩慢甚至宕機的情況,影響人們的購物體驗。因此,對網(wǎng)購系統(tǒng)進(jìn)行分布式拓展及緩存優(yōu)化具有重要意義。
分布式計算技術(shù)最早由OMG(Open Management Group)組織于1992 年提出,這一技術(shù)的出現(xiàn)很大程度上提高了分布式系統(tǒng)的開發(fā)效率。隨著互聯(lián)網(wǎng)與網(wǎng)絡(luò)技術(shù)的迅速發(fā)展和廣泛應(yīng)用,Sun 公司和Microsoft 公司分別推出了應(yīng)用于B/S 架構(gòu)的J2EE 開發(fā)應(yīng)用平臺和面向B/S 應(yīng)用的.NET 開發(fā)應(yīng)用平臺[2-3]。
相比于傳統(tǒng)單機部署的企業(yè)級項目,分布式部署項目方式的出現(xiàn)能夠極大地幫助企業(yè)提高系統(tǒng)的穩(wěn)定性、擴展性和并發(fā)性。人們接觸的架構(gòu)通常是從簡單到復(fù)雜、從單一到復(fù)合不斷改進(jìn)的過程。隨著互聯(lián)網(wǎng)技術(shù)的不斷發(fā)展,可以在分布式架構(gòu)中加入負(fù)載均衡算法、Redis 數(shù)據(jù)庫等技術(shù)以實現(xiàn)對項目的優(yōu)化。李效利等[4]通過分布式網(wǎng)絡(luò)架構(gòu)提高了監(jiān)測平臺的一體化管理;陳鵬煒[5]運用open?resty 及其負(fù)載均衡策略設(shè)計了一個集群實名鑒權(quán)系統(tǒng),相比傳統(tǒng)系統(tǒng)提高了性能;李曉東[6]采用ssm 框架和Nginx 負(fù)載均衡策略緩解了Mysql 數(shù)據(jù)庫的讀取壓力;孔祥真等[7]為了解決網(wǎng)絡(luò)服務(wù)器中高流量不穩(wěn)定的問題,部署Nginx和tomcat 服務(wù)器以提供一個高性能的服務(wù)器解決方案。這些學(xué)者在運用Nginx 負(fù)載均衡時僅僅考慮了應(yīng)對并發(fā)時的服務(wù)器壓力問題,面對數(shù)據(jù)存儲效率問題時也只是簡單地采用本地Mysql 數(shù)據(jù)庫,而面對大量人群訪問數(shù)據(jù)時,其數(shù)據(jù)提取性能并沒有得到有效改善。
在分布式集群中加入Redis 緩存的方式同樣可以提高系統(tǒng)性能。李彥辰等[8]通過設(shè)計Redis 集群的分布式搜索方法提高了連接分析性能;陳清[9]為了應(yīng)對大型機電設(shè)備數(shù)據(jù)交換時出現(xiàn)的問題,將Redis 緩存運用其中,提高了數(shù)據(jù)查詢速度;Li等[10]運用改進(jìn)的一致性哈希算法進(jìn)行過濾系統(tǒng)設(shè)計,提高了Redis 集群的可用性等性能。這些研究通過Redis 集群或者算法優(yōu)化方式提高了數(shù)據(jù)提取性能,然而卻忽略了大量人群訪問服務(wù)器時的壓力問題,當(dāng)服務(wù)器因為壓力過大發(fā)生宕機問題時,數(shù)據(jù)獲取將變得十分困難。
針對以上不足,本文提出了一種新的分布式高可用集群,對之前單一的Nginx 策略或者Redis 集群進(jìn)行改進(jìn),將Nginx 負(fù)載均衡策略、Redis 哨兵集群相結(jié)合,并且加入最新的Nginx lua 緩存技術(shù),改善了之前在實現(xiàn)高可用時數(shù)據(jù)提取效率問題,以及在數(shù)據(jù)提取時所忽略的服務(wù)器壓力過大問題,既保證了集群在高并發(fā)情況下的正常運作,又提升了數(shù)據(jù)提取性能。最后以傳統(tǒng)本地部署的網(wǎng)購系統(tǒng)為背景,結(jié)合新建的集群進(jìn)行試驗驗證,通過得到的參數(shù)證明該集群可提高數(shù)據(jù)提取能力和抗并發(fā)能力。
OpenResty 是一個基于Nginx 與Lua 的高性能Web 平臺,其內(nèi)部有精良的Lua 庫、第三方模塊等依賴項。它可以用來搭建能夠處理高并發(fā)、擴展性極高的動態(tài)Web 應(yīng)用、Web 服務(wù)和動態(tài)網(wǎng)關(guān)[11-13]。Nginx 是一款高性能的HTTP 服務(wù)器/反向代理服務(wù)器及電子郵件(IMAP/POP3)代理服務(wù)器,最高可以承受5 萬的并發(fā)量,而對CPU、內(nèi)存等資源消耗卻非常低,運行十分穩(wěn)定[14-16]。此外,Nginx 在作為Web 服務(wù)器、動態(tài)分離服務(wù)器,以及反向代理服務(wù)器時都發(fā)揮著重要作用。在反向代理方面,Nginx 實現(xiàn)的負(fù)載均衡算法主要有輪詢、輪詢權(quán)值、ip_hash、url_hash(第三方)、fail(第三方)等。Nginx lua 是基于Nginx 協(xié)程機制的一種緩存方式,針對用戶想要獲取的內(nèi)容,使用lua 腳本的方式在Nginx 上完成對應(yīng)業(yè)務(wù)代碼的處理邏輯,可以避免訪問Java服務(wù)器。
Redis 是一個開源的高性能鍵值對(key-value)數(shù)據(jù)庫,根據(jù)官方測試得出50 個并發(fā)執(zhí)行10 000 個請求,讀的速度是11 000 次/s,寫的速度是81 000 次/s[17],且Redis 可以提供多種鍵值數(shù)據(jù)類型如:字符串類型、哈希類型、列表類型等。Redis 是一個緩存的數(shù)據(jù)庫中間件,可以將它設(shè)置為數(shù)據(jù)刷新到磁盤中的策略,可以支持當(dāng)系統(tǒng)對一定數(shù)量key 產(chǎn)生set 操作變化時即刷新一次磁盤,也可以設(shè)置每個1s 或2s 輪詢的方式刷新對應(yīng)的磁盤。因此,Redis 磁盤具備了存儲數(shù)據(jù)庫的能力,但會丟失一定數(shù)量的數(shù)據(jù),可知Redis 也是一個易失性的數(shù)據(jù)存儲。Redis 除讀寫高效外,還可以利用其搭建多節(jié)分布式集群從而提高數(shù)據(jù)讀取效率,同時也極大地提高了系統(tǒng)性能。
目前,網(wǎng)購系統(tǒng)功能趨于穩(wěn)定,對客戶而言主要以訂單功能、登錄功能以及商品詳情頁模塊為主,可以將其拆分放在不同的服務(wù)器中,降低在同一時間多客戶訪問時的訪問壓力;其次可以降低耦合度,有利于后期工程師對網(wǎng)購系統(tǒng)的功能維護(hù)及功能添加。
項目垂直拆分是指按照項目的不同應(yīng)用功能進(jìn)行拆分,將系統(tǒng)不同的功能拆分到不同的服務(wù)器中,各功能之間獨自運行,互不影響,同時提高系統(tǒng)抗并發(fā)能力,具體部署如圖1所示。
Fig.1 Vertical split圖1 垂直拆分
項目水平拆分是指為了提高后期項目維護(hù)效果,按照業(yè)務(wù)對系統(tǒng)進(jìn)行拆分。例如,本系統(tǒng)的業(yè)務(wù)代碼層可以分為Controller 層、dao層、service 層、dataobject 層、model層、接口層,從而降低代碼之間的耦合度,如圖2所示。
Fig.2 Horizontal split圖2 水平拆分
系統(tǒng)優(yōu)化主要是在高并發(fā)優(yōu)化及數(shù)據(jù)提取效率方面,采用Openresty 平臺結(jié)合Redis數(shù)據(jù)庫搭建集群框架。該網(wǎng)購系統(tǒng)采用前后端分離的方式進(jìn)行設(shè)計,前端運用html5標(biāo)準(zhǔn)進(jìn)行編寫,因此首先將Nginx 作為靜態(tài)的Web 服務(wù)器使用,然后將其作為動靜分離的服務(wù)器,對應(yīng)的Nginx 作反向業(yè)務(wù)代理后,可以將對應(yīng)的靜態(tài)請求依舊路由在本地的html 文件中,以靜態(tài)資源請求的方式返回給前端,之后依賴反向代理服務(wù),將動態(tài)請求返回到后端,以完成對應(yīng)的動態(tài)請求代理,以Ajax 請求的方式返回給前端固定的Json參數(shù),實現(xiàn)動靜分離的服務(wù)器使用。Nginx 采用輪詢的負(fù)載均衡方式,將客戶端的請求發(fā)送給Tomcat 服務(wù)器。例如,用戶在獲取商品詳情頁并提取數(shù)據(jù)時,會將其請求發(fā)送到某一臺Tomcat 上,首先從Redis 中提取數(shù)據(jù),若Redis中無數(shù)據(jù),則從Mysql 數(shù)據(jù)庫中進(jìn)行提取,并且查出數(shù)據(jù)后將數(shù)據(jù)緩存到Redis 中,這樣用戶在進(jìn)行第二次查詢時便可從Redis 緩存中直接查詢數(shù)據(jù),提高了用戶對數(shù)據(jù)的查詢效率,如圖3所示。
Fig.3 Distributed high availability cluster圖3 分布式高可用集群
單機部署項目通常部署的是單機版的Redis,其弊端在于對應(yīng)Redis 的單點問題瓶頸難以處理,若對應(yīng)單機節(jié)點的Redis 中斷,則所有的業(yè)務(wù)操作都會消失,且單機版有容量上限,無法滿足高效存儲要求。采取sentinel 的哨兵模式可以很好地解決該問題:引入Redis sentinel 哨兵機制,假設(shè)有兩臺Redis 服務(wù)器,將Redis2 作為Redis1 的從機,Redis 支持主從同步模式,Redis1 可以將數(shù)據(jù)同步給Redis2。理想情況下,當(dāng)網(wǎng)購系統(tǒng)服務(wù)器探測到Redis1 出問題時,可以自動切換到Redis2。然而探測Redis1 是一個繁雜的過程,因為對應(yīng)的分布式環(huán)境十分復(fù)雜,無法明確Redis1 是否為宕機狀態(tài),并且它需要知道切換到哪一臺備機上。因此,引入哨兵機制,它與Redis1、Redis2 都建立了長連接,并且是一個心跳機制,Redis sentinel 清楚地知道Redis1 和Redis2 是處于哪種狀態(tài),當(dāng)項目啟動時無需感知Redis1 和Redis2,只需詢問Redis sentinel 即可知道需要連接哪一臺服務(wù)器。Redis sentinel 是一個單點機器,因此可以完全確定Redis1 和Redis2 哪一臺是主,它可以將Redis1指定為master,將Redis2 指定為slave,一旦確定Redis1 位于master 后,項目服務(wù)器就會連接Redis1 做一個get、set 操作,當(dāng)Redis1 產(chǎn)生異常后,心跳機制就會被破壞掉,Redis sentinel將立刻做一次切換,將Redis2指定為master,Redis1指定為slave,此時Redis sentinel 會通知項目服務(wù)器產(chǎn)生了變化,所部屬項目重新觸發(fā)詢問流程,會將get、set 操作改變到Redis2 去。哨兵機制很好地解決了當(dāng)用戶提取數(shù)據(jù)時,一臺Redis 服務(wù)器出現(xiàn)問題時,用戶無法獲取信息的問題。本文采取的是開啟2 臺Redis 主機、兩臺Redis 從機的方式實現(xiàn)Redis集群,如圖4所示。
Fig.4 Redis cluster圖4 Redis集群
盡管采用了Redis 集群部署,但是面對大量訪問涌入時,服務(wù)器壓力仍然較大,若將Nginx 與Redis 集群聯(lián)系起來,則對數(shù)據(jù)緩存性能有所提升。例如,當(dāng)用戶發(fā)起訪問商品詳情頁請求時,Nginx 收到請求后不會直接鏈路到后端部署的兩臺項目服務(wù)器,它直接連到Redis 從機上,進(jìn)行只讀不寫的操作,同時分擔(dān)了Redis 主機的負(fù)載,若Redis中沒有指定數(shù)據(jù),就回源到項目服務(wù)器上,項目服務(wù)器也對Redis 中的數(shù)據(jù)進(jìn)行判斷,若還是沒有,則回源到Mysql數(shù)據(jù)庫中進(jìn)行數(shù)據(jù)提取,并且放入Redis 中,則下一次客戶將對應(yīng)請求發(fā)送到Nginx 上時就可以直接通過Redis 進(jìn)行“讀”的操作。將Lua 緩存Redis 主從搭配方式相結(jié)合,可以從容應(yīng)對大流量的數(shù)據(jù),提高數(shù)據(jù)提取效率。
本文將網(wǎng)購系統(tǒng)及Nginx、Redis 集群部署到多臺虛擬機Linux 系統(tǒng)中,開啟訪問權(quán)限和防火墻,運用Xshell 文件實現(xiàn)本地Windows 系統(tǒng)與Linux 系統(tǒng)的通信連接,從而可以進(jìn)行對項目的運行操作,如圖5所示。
Fig.5 Project server cluster deployment圖5 項目服務(wù)器集群部署
將本地的網(wǎng)購項目打成jar 包,分別傳輸至2 臺Vm?ware 虛擬機中。將兩臺服務(wù)器靜態(tài)IP 地址分別設(shè)置為192.168.157.138 和192.168.157.139,對應(yīng)端口號都設(shè)置為8 090 端口;再將Openresty 部署到第三臺虛擬機中,設(shè)置靜態(tài)地址為192.168.157.140,點開nginx.conf 文件完成監(jiān)聽端口號、域名ip、負(fù)載均衡輪詢方式等一系列設(shè)置。Redis 集群采用2 主2 從的方式,將其部署到4 臺虛擬機中,分別設(shè)置好靜態(tài)IP 地址,并且開啟端口分別為6 500、6 501、6 502和6 503,通過測試,集群可以正常啟動。再進(jìn)入Openresty服務(wù)器中,新建itemredis.lua 文件,在其中設(shè)置lua 調(diào)用Re?dis 的腳本,同時修改nginx.conf 文件,使其運行時可以啟動相應(yīng)的腳本。部署完所有項目后開啟項目進(jìn)行壓力測試。
本次測試主要驗證項目優(yōu)化后的抗并發(fā)能力及數(shù)據(jù)提取能力,采用jmeter 壓測工具進(jìn)行測試。實驗測試環(huán)境為:i7 10 代/16G 內(nèi)存、Windows10 系統(tǒng)、JDK1.8、Redis4.0、Tomcat8.5、Mysql5.7。
Jmeter 是基于Java 的壓力測試工具,可以用來測試服務(wù)器在不同壓力下的強度與性能[18-19]。使用該測試工具,分別設(shè)置測試用戶在1 000、1 500、2 000 時的并發(fā)量;啟動線程時間為10s,循環(huán)次數(shù)15 次,進(jìn)行壓力測試。具體通過觀察數(shù)據(jù)提取的平均值、中位數(shù)、90%線位、錯誤率以及吞吐量對比出原始網(wǎng)購系統(tǒng)與優(yōu)化后網(wǎng)購系統(tǒng)的性能。圖6——圖8 分別表示在1 000、1 500、2 000 并發(fā)量時的壓測結(jié)果。
Fig.6 1 000 concurrency comparison results圖6 1 000并發(fā)量對比結(jié)果
Fig.7 1 500 concurrency comparison results圖7 1 500并發(fā)量對比結(jié)果
Fig.8 2 000 concurrency comparison results圖8 2 000并發(fā)量對比結(jié)果
測試結(jié)果表明,與傳統(tǒng)本地部署的網(wǎng)購系統(tǒng)相比,優(yōu)化后的網(wǎng)購系統(tǒng)在吞吐量、數(shù)據(jù)響應(yīng)時間上有明顯性能提升,在并發(fā)量為1 000 的情況下,吞吐量相比增加624/s、平均響應(yīng)時間減少72ms;并發(fā)量為1 500的情況下,吞吐量相比增加1 274/s、平均響應(yīng)時間減少151ms;并發(fā)量為2 000的情況下,吞吐量相比增加1 062/s、平均響應(yīng)時間減少135ms。錯誤率方面,并發(fā)量為1 000、1 500 時錯誤率都為0,在并發(fā)量為2 000 時,優(yōu)化后系統(tǒng)的錯誤率仍然低于之前的錯誤率。
本文針對傳統(tǒng)本地部署的網(wǎng)購系統(tǒng)進(jìn)行了項目優(yōu)化及重構(gòu),通過將項目部署到虛擬機服務(wù)器中,并且運用Nginx、Redis 集群及Lua 緩存等技術(shù),構(gòu)建出一個分布式高可用的集群以支撐該系統(tǒng)。通過壓力測試驗證所建集群在控制錯誤率的情況下,該系統(tǒng)的并發(fā)能力及數(shù)據(jù)處理效率都有一定提高,可以更好應(yīng)對高并發(fā)情況下產(chǎn)生的問題。然而,本文并沒有考慮如何應(yīng)對類似“雙十一”活動的瞬時并發(fā)問題,這有待進(jìn)一步研究解決。