張 杰 劉 凱 周立軍
(海軍航空大學(xué)航空基礎(chǔ)學(xué)院 煙臺(tái) 264001)
隨著互聯(lián)網(wǎng)信息技術(shù)的發(fā)展,校園信息化程度不斷提升,傳統(tǒng)的信息系統(tǒng)一般可抽象為三層相對(duì)獨(dú)立模型:表示層、應(yīng)用層和數(shù)據(jù)庫(kù)層,并且在維護(hù)、部署、穩(wěn)定性等方面具有一定的優(yōu)勢(shì),但是在并發(fā)性和擴(kuò)展性方面,存在難以逾越瓶頸。當(dāng)今移動(dòng)互聯(lián)網(wǎng)高速發(fā)展已經(jīng)深刻影響數(shù)字化校園的系統(tǒng)架構(gòu),尤其時(shí)移動(dòng)互聯(lián)網(wǎng)的廣泛應(yīng)用,用戶前端展示與訪問已經(jīng)不再局限于單一的Web、Win-Form、APP等形式[1],甚至包括對(duì)第三方開放的擴(kuò)展服務(wù),此時(shí)信息系統(tǒng)的并發(fā)訪問能力將成為每一個(gè)系統(tǒng)必須面對(duì)的關(guān)鍵問題。
本文主要闡述了信息系統(tǒng)高并發(fā)訪問涉及的相關(guān)技術(shù),針對(duì)數(shù)據(jù)庫(kù)讀寫系統(tǒng)瓶頸,即數(shù)據(jù)庫(kù)讀寫屬于磁盤IO,性能低[2~3],分析了選課系統(tǒng)中高并發(fā)的功能點(diǎn),通過將選課部分?jǐn)?shù)據(jù)和業(yè)務(wù)邏輯轉(zhuǎn)移到內(nèi)存緩存,并搭建了一個(gè)基于Redis高速讀寫的高并發(fā)選課系統(tǒng),實(shí)現(xiàn)了選課信息發(fā)布、學(xué)生選課及課表查詢等功能,通過實(shí)驗(yàn)表明:采用Redis的選課系統(tǒng)在高并發(fā)訪問性能方面具有明顯優(yōu)勢(shì),響應(yīng)速度和并發(fā)處理能力有較大提升。
選課計(jì)劃系統(tǒng)用于根據(jù)教學(xué)計(jì)劃、教學(xué)資源等,每周制定的選課計(jì)劃及相關(guān)信息,供學(xué)員在除正課之外的時(shí)間進(jìn)行自主選擇,系統(tǒng)分為包括教務(wù)處、教研室、學(xué)生管理單位及學(xué)生等角色,其中本文重點(diǎn)分析選課子系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)。
圖1 系統(tǒng)功能設(shè)計(jì)
選課子系統(tǒng)主要功能包括選課批次管理、選課計(jì)劃管理、學(xué)員選課、選課排課、系統(tǒng)管理等,其中,學(xué)員選課包括多種展示方式,如Web網(wǎng)頁(yè)、APP終端、Win-Form程序等形式,并且當(dāng)選課的課程表發(fā)布時(shí),會(huì)存在短時(shí)間內(nèi)高并發(fā)訪問操作情況發(fā)生,并且對(duì)系統(tǒng)的穩(wěn)定性與并發(fā)處理能力要求較高,為了提升系統(tǒng)擴(kuò)展性、降低程序耦合度,系統(tǒng)設(shè)計(jì)實(shí)現(xiàn)時(shí)將前端顯示和業(yè)務(wù)處理分離。
圖2 選課系統(tǒng)總體架構(gòu)
系統(tǒng)功能按照服務(wù)進(jìn)行拆分,用戶經(jīng)過授權(quán)后,可調(diào)用選課系統(tǒng)開放的服務(wù)完成相應(yīng)功能,如選課業(yè)務(wù)中學(xué)員經(jīng)登陸驗(yàn)證后,通過RESTful API調(diào)用系統(tǒng)后端選課服務(wù)。
本文提出的高并發(fā)系統(tǒng)設(shè)計(jì)方法涉及的關(guān)鍵技術(shù)主要包括:Redis緩存熱點(diǎn)數(shù)據(jù)和Quartz任務(wù)調(diào)度定時(shí)加載熱點(diǎn)數(shù)據(jù)。
Redis是一種基于內(nèi)存的高性能Key-Value數(shù)據(jù)庫(kù)[4],在解決高并發(fā)與大數(shù)據(jù)等應(yīng)用場(chǎng)景,可用于提升熱點(diǎn)數(shù)據(jù)訪問的性能,Redis與傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)有所不同,支持鏈表(List)、字符串(String)、集合(set)和有序集合(zset)等4種抽象數(shù)據(jù)結(jié)構(gòu),此外,還支持?jǐn)?shù)據(jù)計(jì)算中常用的集合并、交和補(bǔ)集等操作及多種排序算法[5],Redis所有數(shù)據(jù)都是保存在內(nèi)存中,通過不定期半持久化模式(異步方式保存到磁盤上)和持久化模式等操作,提供周期性地把數(shù)據(jù)操作寫到AOF文件中,數(shù)據(jù)恢復(fù)時(shí)將數(shù)據(jù)加載到內(nèi)存中,保障數(shù)據(jù)安全,在Redis中緩存的選課數(shù)據(jù)及所使用數(shù)據(jù)類型如表1所示。
表1 熱點(diǎn)數(shù)據(jù)緩存及類型說明
Quartz是一個(gè)開源完全作業(yè)調(diào)度框架,將任務(wù)調(diào)度的相關(guān)問題進(jìn)行了抽象,提出了調(diào)度器、觸發(fā)器和任務(wù)等三個(gè)核心概念,分別對(duì)應(yīng)Scheduler、Trigger和Job,為應(yīng)用程序進(jìn)行作業(yè)調(diào)度提供了簡(jiǎn)單強(qiáng)大的機(jī)制,當(dāng)一個(gè)在預(yù)先確定的任務(wù)(如定時(shí)發(fā)布選課課表)的時(shí)間到達(dá)時(shí),負(fù)責(zé)執(zhí)行指定任務(wù)或功能(將選課數(shù)據(jù)加載到Redis數(shù)據(jù)緩存),Quartz實(shí)現(xiàn)上述功能的主要接口是Scheduler,通過SchedulerFactory創(chuàng)建一個(gè)Scheduler任務(wù)調(diào)度實(shí)例,它提供了的操作包括:將任務(wù)納入日程或者從日程中取消、開始/停止/暫停日程進(jìn)度等,其中Trigger和Job是任務(wù)調(diào)度的元數(shù)據(jù),Scheduler是實(shí)際執(zhí)行調(diào)度的控制器,Trigger是用于定義調(diào)度時(shí)間的元素,描述觸發(fā)Job執(zhí)行的時(shí)間觸發(fā)規(guī)則,Job用于表示被調(diào)度的任務(wù),按狀態(tài)可分為無狀態(tài)(stateless)和有狀態(tài)(stateful)兩種類型。對(duì)于同一個(gè)trigger來說,有狀態(tài)的job不能被并行執(zhí)行,只有上次觸發(fā)的任務(wù)被執(zhí)行完之后,才能觸發(fā)下次執(zhí)行[6~7]。
Quartz作業(yè)調(diào)度框架與Java內(nèi)置java.util.Tim?er定時(shí)器相比,除了具有可持久化、靈活、功能強(qiáng)大等優(yōu)點(diǎn),Quartz可通過cron表達(dá)式精確到特定時(shí)間執(zhí)行,且Quartz每次執(zhí)行任務(wù)都將創(chuàng)建一個(gè)新的對(duì)象,所以某一次執(zhí)行任務(wù)過程中拋出異常,對(duì)下一次任務(wù)的執(zhí)行不會(huì)產(chǎn)生影響,Timer在執(zhí)行某個(gè)任務(wù)程中拋出異常,則整個(gè)定時(shí)器生命周期就結(jié)束,將失去定時(shí)器的作用[8]。
選課子系統(tǒng)核心業(yè)務(wù)是學(xué)員選課,具體業(yè)務(wù)流程包括:
1)教研室為單位,提交下一周自主學(xué)習(xí)訓(xùn)練任務(wù),教研室應(yīng)主要對(duì)本學(xué)期開設(shè)課程安排輔導(dǎo)答疑和實(shí)驗(yàn)室自習(xí)等自主學(xué)習(xí)任務(wù);
2)教科處審核并發(fā)布自主學(xué)習(xí)訓(xùn)練計(jì)劃;
3)學(xué)員通過個(gè)人賬戶登錄校園網(wǎng)(周末),制定自主學(xué)習(xí)計(jì)劃;
4)教科處進(jìn)入選課管理進(jìn)行學(xué)員調(diào)整和選開課排課;
5)發(fā)布每周自主學(xué)習(xí)選排課供查詢、下載、打印。
圖3 系統(tǒng)高并發(fā)核心業(yè)務(wù)分析
通過分析選課子系統(tǒng)核心業(yè)務(wù)流程中存在高并發(fā)的模塊主要涉及:學(xué)生選課的課表信息(按周進(jìn)行發(fā)布)和學(xué)員個(gè)人課表查詢,在系統(tǒng)實(shí)現(xiàn)時(shí),采用Quartz任務(wù)調(diào)度,在給定時(shí)間(每周六24點(diǎn))按教學(xué)周將選課數(shù)據(jù)從Mysql數(shù)據(jù)庫(kù)緩存至Redis服務(wù)器,學(xué)生按照教學(xué)周查詢已經(jīng)開放的課表時(shí)直接從Redis內(nèi)存中讀取數(shù)據(jù),較大程度提升客戶端相應(yīng)速度和數(shù)據(jù)處理能力[9]。
圖4 高并發(fā)選課業(yè)務(wù)流程
選課業(yè)務(wù)中存在典型的高并發(fā)應(yīng)用場(chǎng)景,供學(xué)員選課的信息在特定時(shí)間發(fā)布,學(xué)員會(huì)在課程發(fā)布后集中完成選課操作,由于選課課程有明確人數(shù)限制,高并發(fā)選課操作發(fā)生時(shí),頻繁的數(shù)據(jù)庫(kù)查詢和寫入操作,容易導(dǎo)致數(shù)據(jù)庫(kù)宕機(jī)[10],為了緩解數(shù)據(jù)庫(kù)服務(wù)器的處理能力與高并發(fā)訪問的矛盾,系統(tǒng)采用以下設(shè)計(jì)方法進(jìn)行改進(jìn):
1)由定時(shí)器按教學(xué)周,讀取選課課程數(shù)據(jù)進(jìn)行Redis緩存;
2)當(dāng)學(xué)員選擇一門課程時(shí),首先檢查是否已選過,并檢查是否已經(jīng)超過人數(shù)限制,滿足條件進(jìn)行選課,否則返回選課失敗及原因;
3)實(shí)現(xiàn)選課操作,生成學(xué)員選課記錄并存入到Redis,更新選課人數(shù);
4)使用Redis消息隊(duì)列發(fā)送選課記錄,消息接收后完成存入Mysql數(shù)據(jù)庫(kù)。
在第2)、3)步驟完成的動(dòng)作,必須滿足事務(wù)性和原子性,否則在高并發(fā)情況下,無法保證數(shù)據(jù)一致性,但Redis為了保證性能,其事務(wù)具有一定局限性(如不支持回滾)[11~12],為了解決這個(gè)問題,可采用Lua編寫腳本,提交到Redis執(zhí)行,Redis會(huì)將整個(gè)Lua腳本作為一個(gè)整體執(zhí)行,在執(zhí)行過程中不會(huì)被其他命令插入(Redis具有單線程特性),第2)、3)步驟對(duì)應(yīng)的Lua腳本如下:
通過將Lua腳本嵌入到選課服務(wù)端,當(dāng)學(xué)員選課時(shí)調(diào)用Lua腳本,借助Lua腳本,Redis將多個(gè)查詢、修改等動(dòng)作包裝成一個(gè)具有原子性操作,在提升了性能的同時(shí),可有效保證數(shù)據(jù)一致性。
在高并發(fā)情況下,如集中學(xué)員選課場(chǎng)景,容易出現(xiàn)多人同時(shí)對(duì)已選課人數(shù)進(jìn)行查詢與修改,為了保證數(shù)據(jù)一致性,防止出現(xiàn)超出選課人數(shù)限制的情況發(fā)生,傳統(tǒng)實(shí)現(xiàn)方法是對(duì)該部分?jǐn)?shù)據(jù)進(jìn)行加鎖(如排它鎖、樂觀鎖、悲觀鎖等)[13],但普遍存在性能上的缺失,導(dǎo)致系統(tǒng)并發(fā)處理能力降低[14]。通過使用Redis將選課相關(guān)熱點(diǎn)數(shù)據(jù)存入緩存,建立Tom?cat+Redis+Mysql建立高并發(fā)選課系統(tǒng),緩解高并發(fā)時(shí)數(shù)據(jù)庫(kù)的訪問壓力,并提升處理性能。
為了驗(yàn)證系統(tǒng)構(gòu)架在并發(fā)處理能力方面的性能,選擇Apache JMeter進(jìn)行負(fù)載功能測(cè)試和性能測(cè)試,相比其他測(cè)試軟件(如Loadrunner),JMeter具有輕量、開源等優(yōu)勢(shì)[15~16]。分別設(shè)計(jì)兩種方案:傳統(tǒng)數(shù)據(jù)庫(kù)(方案1)與Redis數(shù)據(jù)緩存(方案2)方式分別完成選課操作,使用JMeter建立用戶線程方式分別模擬500、1000、2000用戶在1s內(nèi)并發(fā)訪問選課系統(tǒng),如表2所示,實(shí)驗(yàn)環(huán)境軟件硬件條件:i5/8G內(nèi) 存 、Windows 7/Jdk1.8/Tomcat8.5/Jmeter5.0/Je?dis2.4/Mysql5.6。
表2 Jmeter性能統(tǒng)計(jì)數(shù)據(jù)(時(shí)間單位:ms)
實(shí)驗(yàn)數(shù)據(jù)表明:采用Redis數(shù)據(jù)緩存方式,在并發(fā)處理能力的各項(xiàng)指標(biāo)提升明顯,尤其是吞吐量(單位時(shí)間處理請(qǐng)求的數(shù)量)可提升40%左右。
本文分析了當(dāng)前信息系統(tǒng)在應(yīng)對(duì)高并發(fā)訪問存在的瓶頸,以典型的學(xué)員選課應(yīng)用場(chǎng)景為例,從系統(tǒng)設(shè)計(jì)到核心功能實(shí)現(xiàn),闡述了使用Redis將選課等熱點(diǎn)數(shù)據(jù)進(jìn)行緩存,針對(duì)選課并發(fā)操作的過程,采用Lua腳本實(shí)現(xiàn)多個(gè)動(dòng)作(Redis查詢、修改等)封裝成一個(gè)原子性操作[17],在提升Redis操作性能同時(shí),有效避免數(shù)據(jù)不一致性;最后,通過使用Jmeter性能測(cè)試工具,對(duì)文中提出數(shù)據(jù)緩存方案與傳統(tǒng)數(shù)據(jù)庫(kù)實(shí)現(xiàn)方式對(duì)比進(jìn)行測(cè)試,測(cè)試結(jié)果表明Redis數(shù)據(jù)緩存方式并發(fā)性能明顯提升,本文使用Quartz和Redis提升系統(tǒng)并發(fā)能力的方法,具有一定的通用性和借鑒作用。