焦 宇,李 民,王 歡,余開朝
(昆明理工大學機電工程學院,云南昆明 650500)
隨著互聯網技術的迅速發展,人們與網絡的關系日益密切,互聯網帶給人們一種全新的生活方式,如網上購物、滴滴打車、學習網課等都為人們的生活帶來了全新的體驗,而近些年的網上購物更是發展得如火如荼[1]。網購系統的出現極大地改善了人們的購物體驗,雖然不能完全替代線下購物,但已逐漸成為人們主流的購物方式。由此,對網購系統設計要求隨之提高,如遇到某些購物活動,大量客戶進入系統進行購物,傳統的單機部署式網購系統已經無法承受如此高的并發量,會發生提取數據緩慢甚至宕機的情況,影響人們的購物體驗。因此,對網購系統進行分布式拓展及緩存優化具有重要意義。
分布式計算技術最早由OMG(Open Management Group)組織于1992 年提出,這一技術的出現很大程度上提高了分布式系統的開發效率。隨著互聯網與網絡技術的迅速發展和廣泛應用,Sun 公司和Microsoft 公司分別推出了應用于B/S 架構的J2EE 開發應用平臺和面向B/S 應用的.NET 開發應用平臺[2-3]。
相比于傳統單機部署的企業級項目,分布式部署項目方式的出現能夠極大地幫助企業提高系統的穩定性、擴展性和并發性。人們接觸的架構通常是從簡單到復雜、從單一到復合不斷改進的過程。隨著互聯網技術的不斷發展,可以在分布式架構中加入負載均衡算法、Redis 數據庫等技術以實現對項目的優化。李效利等[4]通過分布式網絡架構提高了監測平臺的一體化管理;陳鵬煒[5]運用open?resty 及其負載均衡策略設計了一個集群實名鑒權系統,相比傳統系統提高了性能;李曉東[6]采用ssm 框架和Nginx 負載均衡策略緩解了Mysql 數據庫的讀取壓力;孔祥真等[7]為了解決網絡服務器中高流量不穩定的問題,部署Nginx和tomcat 服務器以提供一個高性能的服務器解決方案。這些學者在運用Nginx 負載均衡時僅僅考慮了應對并發時的服務器壓力問題,面對數據存儲效率問題時也只是簡單地采用本地Mysql 數據庫,而面對大量人群訪問數據時,其數據提取性能并沒有得到有效改善。
在分布式集群中加入Redis 緩存的方式同樣可以提高系統性能。李彥辰等[8]通過設計Redis 集群的分布式搜索方法提高了連接分析性能;陳清[9]為了應對大型機電設備數據交換時出現的問題,將Redis 緩存運用其中,提高了數據查詢速度;Li等[10]運用改進的一致性哈希算法進行過濾系統設計,提高了Redis 集群的可用性等性能。這些研究通過Redis 集群或者算法優化方式提高了數據提取性能,然而卻忽略了大量人群訪問服務器時的壓力問題,當服務器因為壓力過大發生宕機問題時,數據獲取將變得十分困難。
針對以上不足,本文提出了一種新的分布式高可用集群,對之前單一的Nginx 策略或者Redis 集群進行改進,將Nginx 負載均衡策略、Redis 哨兵集群相結合,并且加入最新的Nginx lua 緩存技術,改善了之前在實現高可用時數據提取效率問題,以及在數據提取時所忽略的服務器壓力過大問題,既保證了集群在高并發情況下的正常運作,又提升了數據提取性能。最后以傳統本地部署的網購系統為背景,結合新建的集群進行試驗驗證,通過得到的參數證明該集群可提高數據提取能力和抗并發能力。
OpenResty 是一個基于Nginx 與Lua 的高性能Web 平臺,其內部有精良的Lua 庫、第三方模塊等依賴項。它可以用來搭建能夠處理高并發、擴展性極高的動態Web 應用、Web 服務和動態網關[11-13]。Nginx 是一款高性能的HTTP 服務器/反向代理服務器及電子郵件(IMAP/POP3)代理服務器,最高可以承受5 萬的并發量,而對CPU、內存等資源消耗卻非常低,運行十分穩定[14-16]。此外,Nginx 在作為Web 服務器、動態分離服務器,以及反向代理服務器時都發揮著重要作用。在反向代理方面,Nginx 實現的負載均衡算法主要有輪詢、輪詢權值、ip_hash、url_hash(第三方)、fail(第三方)等。Nginx lua 是基于Nginx 協程機制的一種緩存方式,針對用戶想要獲取的內容,使用lua 腳本的方式在Nginx 上完成對應業務代碼的處理邏輯,可以避免訪問Java服務器。
Redis 是一個開源的高性能鍵值對(key-value)數據庫,根據官方測試得出50 個并發執行10 000 個請求,讀的速度是11 000 次/s,寫的速度是81 000 次/s[17],且Redis 可以提供多種鍵值數據類型如:字符串類型、哈希類型、列表類型等。Redis 是一個緩存的數據庫中間件,可以將它設置為數據刷新到磁盤中的策略,可以支持當系統對一定數量key 產生set 操作變化時即刷新一次磁盤,也可以設置每個1s 或2s 輪詢的方式刷新對應的磁盤。因此,Redis 磁盤具備了存儲數據庫的能力,但會丟失一定數量的數據,可知Redis 也是一個易失性的數據存儲。Redis 除讀寫高效外,還可以利用其搭建多節分布式集群從而提高數據讀取效率,同時也極大地提高了系統性能。
目前,網購系統功能趨于穩定,對客戶而言主要以訂單功能、登錄功能以及商品詳情頁模塊為主,可以將其拆分放在不同的服務器中,降低在同一時間多客戶訪問時的訪問壓力;其次可以降低耦合度,有利于后期工程師對網購系統的功能維護及功能添加。
項目垂直拆分是指按照項目的不同應用功能進行拆分,將系統不同的功能拆分到不同的服務器中,各功能之間獨自運行,互不影響,同時提高系統抗并發能力,具體部署如圖1所示。

Fig.1 Vertical split圖1 垂直拆分
項目水平拆分是指為了提高后期項目維護效果,按照業務對系統進行拆分。例如,本系統的業務代碼層可以分為Controller 層、dao層、service 層、dataobject 層、model層、接口層,從而降低代碼之間的耦合度,如圖2所示。

Fig.2 Horizontal split圖2 水平拆分
系統優化主要是在高并發優化及數據提取效率方面,采用Openresty 平臺結合Redis數據庫搭建集群框架。該網購系統采用前后端分離的方式進行設計,前端運用html5標準進行編寫,因此首先將Nginx 作為靜態的Web 服務器使用,然后將其作為動靜分離的服務器,對應的Nginx 作反向業務代理后,可以將對應的靜態請求依舊路由在本地的html 文件中,以靜態資源請求的方式返回給前端,之后依賴反向代理服務,將動態請求返回到后端,以完成對應的動態請求代理,以Ajax 請求的方式返回給前端固定的Json參數,實現動靜分離的服務器使用。Nginx 采用輪詢的負載均衡方式,將客戶端的請求發送給Tomcat 服務器。例如,用戶在獲取商品詳情頁并提取數據時,會將其請求發送到某一臺Tomcat 上,首先從Redis 中提取數據,若Redis中無數據,則從Mysql 數據庫中進行提取,并且查出數據后將數據緩存到Redis 中,這樣用戶在進行第二次查詢時便可從Redis 緩存中直接查詢數據,提高了用戶對數據的查詢效率,如圖3所示。

Fig.3 Distributed high availability cluster圖3 分布式高可用集群
單機部署項目通常部署的是單機版的Redis,其弊端在于對應Redis 的單點問題瓶頸難以處理,若對應單機節點的Redis 中斷,則所有的業務操作都會消失,且單機版有容量上限,無法滿足高效存儲要求。采取sentinel 的哨兵模式可以很好地解決該問題:引入Redis sentinel 哨兵機制,假設有兩臺Redis 服務器,將Redis2 作為Redis1 的從機,Redis 支持主從同步模式,Redis1 可以將數據同步給Redis2。理想情況下,當網購系統服務器探測到Redis1 出問題時,可以自動切換到Redis2。然而探測Redis1 是一個繁雜的過程,因為對應的分布式環境十分復雜,無法明確Redis1 是否為宕機狀態,并且它需要知道切換到哪一臺備機上。因此,引入哨兵機制,它與Redis1、Redis2 都建立了長連接,并且是一個心跳機制,Redis sentinel 清楚地知道Redis1 和Redis2 是處于哪種狀態,當項目啟動時無需感知Redis1 和Redis2,只需詢問Redis sentinel 即可知道需要連接哪一臺服務器。Redis sentinel 是一個單點機器,因此可以完全確定Redis1 和Redis2 哪一臺是主,它可以將Redis1指定為master,將Redis2 指定為slave,一旦確定Redis1 位于master 后,項目服務器就會連接Redis1 做一個get、set 操作,當Redis1 產生異常后,心跳機制就會被破壞掉,Redis sentinel將立刻做一次切換,將Redis2指定為master,Redis1指定為slave,此時Redis sentinel 會通知項目服務器產生了變化,所部屬項目重新觸發詢問流程,會將get、set 操作改變到Redis2 去。哨兵機制很好地解決了當用戶提取數據時,一臺Redis 服務器出現問題時,用戶無法獲取信息的問題。本文采取的是開啟2 臺Redis 主機、兩臺Redis 從機的方式實現Redis集群,如圖4所示。

Fig.4 Redis cluster圖4 Redis集群
盡管采用了Redis 集群部署,但是面對大量訪問涌入時,服務器壓力仍然較大,若將Nginx 與Redis 集群聯系起來,則對數據緩存性能有所提升。例如,當用戶發起訪問商品詳情頁請求時,Nginx 收到請求后不會直接鏈路到后端部署的兩臺項目服務器,它直接連到Redis 從機上,進行只讀不寫的操作,同時分擔了Redis 主機的負載,若Redis中沒有指定數據,就回源到項目服務器上,項目服務器也對Redis 中的數據進行判斷,若還是沒有,則回源到Mysql數據庫中進行數據提取,并且放入Redis 中,則下一次客戶將對應請求發送到Nginx 上時就可以直接通過Redis 進行“讀”的操作。將Lua 緩存Redis 主從搭配方式相結合,可以從容應對大流量的數據,提高數據提取效率。
本文將網購系統及Nginx、Redis 集群部署到多臺虛擬機Linux 系統中,開啟訪問權限和防火墻,運用Xshell 文件實現本地Windows 系統與Linux 系統的通信連接,從而可以進行對項目的運行操作,如圖5所示。

Fig.5 Project server cluster deployment圖5 項目服務器集群部署
將本地的網購項目打成jar 包,分別傳輸至2 臺Vm?ware 虛擬機中。將兩臺服務器靜態IP 地址分別設置為192.168.157.138 和192.168.157.139,對應端口號都設置為8 090 端口;再將Openresty 部署到第三臺虛擬機中,設置靜態地址為192.168.157.140,點開nginx.conf 文件完成監聽端口號、域名ip、負載均衡輪詢方式等一系列設置。Redis 集群采用2 主2 從的方式,將其部署到4 臺虛擬機中,分別設置好靜態IP 地址,并且開啟端口分別為6 500、6 501、6 502和6 503,通過測試,集群可以正常啟動。再進入Openresty服務器中,新建itemredis.lua 文件,在其中設置lua 調用Re?dis 的腳本,同時修改nginx.conf 文件,使其運行時可以啟動相應的腳本。部署完所有項目后開啟項目進行壓力測試。
本次測試主要驗證項目優化后的抗并發能力及數據提取能力,采用jmeter 壓測工具進行測試。實驗測試環境為:i7 10 代/16G 內存、Windows10 系統、JDK1.8、Redis4.0、Tomcat8.5、Mysql5.7。
Jmeter 是基于Java 的壓力測試工具,可以用來測試服務器在不同壓力下的強度與性能[18-19]。使用該測試工具,分別設置測試用戶在1 000、1 500、2 000 時的并發量;啟動線程時間為10s,循環次數15 次,進行壓力測試。具體通過觀察數據提取的平均值、中位數、90%線位、錯誤率以及吞吐量對比出原始網購系統與優化后網購系統的性能。圖6——圖8 分別表示在1 000、1 500、2 000 并發量時的壓測結果。

Fig.6 1 000 concurrency comparison results圖6 1 000并發量對比結果

Fig.7 1 500 concurrency comparison results圖7 1 500并發量對比結果

Fig.8 2 000 concurrency comparison results圖8 2 000并發量對比結果
測試結果表明,與傳統本地部署的網購系統相比,優化后的網購系統在吞吐量、數據響應時間上有明顯性能提升,在并發量為1 000 的情況下,吞吐量相比增加624/s、平均響應時間減少72ms;并發量為1 500的情況下,吞吐量相比增加1 274/s、平均響應時間減少151ms;并發量為2 000的情況下,吞吐量相比增加1 062/s、平均響應時間減少135ms。錯誤率方面,并發量為1 000、1 500 時錯誤率都為0,在并發量為2 000 時,優化后系統的錯誤率仍然低于之前的錯誤率。
本文針對傳統本地部署的網購系統進行了項目優化及重構,通過將項目部署到虛擬機服務器中,并且運用Nginx、Redis 集群及Lua 緩存等技術,構建出一個分布式高可用的集群以支撐該系統。通過壓力測試驗證所建集群在控制錯誤率的情況下,該系統的并發能力及數據處理效率都有一定提高,可以更好應對高并發情況下產生的問題。然而,本文并沒有考慮如何應對類似“雙十一”活動的瞬時并發問題,這有待進一步研究解決。