黃素萍,劉敏娜,朱亞兵,田知佩
(咸陽師范學院 計算機學院,陜西 咸陽 712000)
便捷的網絡使得互聯網用戶基數越來越大,并發請求量隨之增多。當大量用戶通過網絡同時訪問同一站點時, 就會出現高并發的情況,Web 系統性能瓶頸也會隨之出現。相對于用戶對服務性能提出的更高要求, 服務器對Web 訪問請求處理速度的提升卻遠遠低于應用服務的增長需求,單靠提升服務器硬件性能已不能滿足一些Web 應用系統[1]的需求。 目前,大型分布式系統采用的高并發解決方案并不適用部署在單服務器上的中小型Web 應用。通過集群等方式處理高并發流量的方案成本過高[2]。 因此,需要為部署在單服務器的Web 應用提出可用的網絡流量高并發優化處理方案。
Web 應用系統在大量用戶進行訪問的情況下,易出現系統響應的時間過長、系統崩潰和癱瘓等現象。同時,隨著社會不斷發展,使用互聯網的用戶越來越多。 所以,要對Web 應用系統功能進行優化,以保證Web 應用系統的服務質量。
解決高并發的一些常用關鍵技術包含:
(1)緩存技術。它是把數據庫的數據存入緩存,請求不經過數據庫,直接從緩存中讀取數據,能夠大大提高效率。 其中,Redis 是單線程的 NoSQL[3]技術,單線程注定了它是線程安全的。并且,它作為內存技術, 訪問和處理速度都遠大于如MySQL 等硬盤技術。
(2)消息隊列技術。RabbitMQ 是目前使用最多的消息隊列。它典型的使用場景就是在大量訪問涌入時,服務器可能無法及時處理需要進行業務削峰時發揮作用。
(3)Zookeeper 技術。Zookeeper 是一門分布式技術。 它通過序列節點機制實現分布式鎖,能夠解決多個線程競爭同一個共享資源時的線程安全問題。
通過研究Web 系統,為了應對高并發問題,可將結合 Redis 緩存、RabbitMQ 消息隊列、Zookeeper的不可重入鎖等中間件技術的實現方案, 應用于Web 應用系統產生高并發訪問功能的模塊,以保證系統的快速響應,解決異步消息、流量削峰和線程安全等問題。 其中,使用Redis 對信息存取處理進行緩存。 同時, 結合RabbitMQ 消息隊列技術,把Redis 處理的大量請求,不直接請求數據庫訪問,采用異步操作方式,先進入消息隊列,然后排隊處理,將瞬間并發訪問變成一段時間正常訪問數據庫。其中,消息隊列可以解決應用耦合、異步消息、流量削峰等的問題,實現系統的高性能、高可用和可伸縮等特點。 另外,通過Zookeeper 實現一個分布式鎖來解決線程安全問題。
為應用以上高并發技術解決方案, 現設計實現一個商品秒殺系統。該系統的用戶分為兩類:管理員和商品秒殺用戶。系統的業務功能有秒殺管理、商品管理、訂單管理、客戶管理、人員管理、購物中心和個人中心等功能模塊。 系統功能模塊如圖1 所示。

圖1 系統功能模塊Figure 1 System function module
系統中,管理員功能模塊分為秒殺管理、訂單管理、商品管理、客戶管理和人員管理模塊。
(1)秒殺管理:分為秒殺信息的添加、修改、刪除和查詢功能。
(2)商品管理: 包括商品基本信息管理、商品類別信息管理和庫存管理。
(3)訂單管理:包括訂單的查詢、刪除、出貨功能。
(4)客戶管理:包含對客戶信息的查看、刪除功能。
(5)人員管理:包括管理員的信息查詢、添加、刪除和修改。
商品秒殺用戶功能模塊分為注冊登錄、購物中心和人個中心。
(1)注冊登錄:未注冊用戶可進行信息填寫并注冊,已經注冊用戶可輸入賬號、密碼、驗證碼進行登錄。
(2)購物中心:包括所有商品、限時搶購、即將開始商品查看功能。
(3)個人中心:包括我的訂單、地址管理、個人信息功能。
本系統設計的數據庫實體主要有商品信息、庫存信息、訂單信息、秒殺活動信息等。 針對這些實體, 商品秒殺系統的數據庫創建了對應的數據表。其中,與秒殺功能有關的主要表字段設計如下:
(1)商品數據表:字段有ID、商品名、類別ID、圖片地址、是否上架、商品描述、差價、創建時間、更新時間、邏輯刪除。
(2)庫存數據表:字段有 ID、商品 ID、進價、成本價、售價、數量、預警量(默認10)、快遞費用、是否刪除。
(3)秒殺活動數據表:字段有ID、庫存ID、收藏人數、剩余數量、總數、銷售價格、開始時間、結束時間、創建時間、修改時間、是否刪除。
系統中包含多個模塊,每個功能模塊又包含多個子功能。本系統主要研究的是處理高并發功能的實現,下面將著重闡述應用高并發技術方案的核心模塊——商品秒殺用戶購物中心模塊。購物中心模塊包括:商品瀏覽、商品查詢、限時搶購三個功能。限時搶購功能的設計中應用了高并發處理方案,其他功能不再贅述。
限時搶購是出現高并發的核心功能。它涉及大量用戶同時操作時的庫存安全、系統可用性、單用戶多次操作的冪等性、要避免用戶點擊一次產生多筆相同訂單和商品超賣等問題。該功能的實現邏輯是,當秒殺用戶參與秒殺操作提交請求時,系統程序首先判斷當前商品的庫存是否充足。若庫存不充足,返回商品售空提示;若庫存充足,程序申請一個基于Zookeeper 的不可重入鎖,如果申請成功進入隊列排隊等待執行,否則返回秒殺失敗提示。 隊列中的任務會依次執行, 執行到的任務會先從Redis拿出進入隊列前生成的訂單ID, 進行實際的下單操作,下單成功后將保存數據庫的操作放入消息隊列,返回下單成功提示,并跳轉到支付界面。功能實現的流程如圖2 所示。

圖2 限時搶購功能流程圖Figure 2 The flow chart of the limited-time snap-up function
由于本系統是使用SpringBoot 框架來實現的,在此功能的實現中,首先在SpringBoot 框架的pom.xml 配置文件里加入依賴整合, 使用Redis 對商品庫存進行緩存,以減小查詢數據庫的壓力。 用戶每次進行商品秒殺時, 系統程序通過調用AddOrder-Controller 添加訂單控制類的 killGoods()方法,使用 其 中 的 語 句 redisTemplate.opsForValue ().get(RedisEnum.USER_KILL_NUM + id + goodsId)從緩存中獲取商品庫存。但這一步并不能完全解決此時的高并發問題,還需要應用RabbitMQ 消息隊列技術,通過Redis 的大量請求,不直接請求數據庫下單采用異步下單方式,調用rabbitTemplate. convertAndSend()方法先進入消息隊列,然后排隊消費,可將瞬間并發訪問變成一段時間正常訪問數據庫。 同時,通過Zookeeper 的序列節點機制來實現一個分布式鎖, 調用AddOrderServiceImpl 添加訂單服務類的killOrder()方法。 在該方法使用lock.acquire(60,TimeUnit.SECONDS)的結果來判斷是否獲取到鎖,解決多個線程競爭同一個共享資源時候的線程安全問題。 基于SpringBoot 框架,系統實現限時搶購功能的業務關系如圖3 所示。

圖3 限時搶購功能實現的業務關系Figure 3 The business relationship of the flash sale function
通過以上幾個環節, 本功能將Redis、Rabbit-MQ、Zookeeper 這幾種中間件技術應用到具體實現中,解決了用戶在商品秒殺產生高并發情況下的異步消息、流量削峰和線程安全問題,實現了系統的快速響應。
在系統開發完成之后, 使用了開源測試工具JMeter 對系統實現高并發處理商品秒殺功能模塊中的限時搶購功能進行了性能測試。JMeter 是開源測試工具,其運行原理是通過線程組來驅動多個線程的方式運行,可以進行接口測試、性能測試、接口及性能的自動化測試。
在JMeter 中, 通過不斷增加線程數模擬多個用戶同時秒殺同一件商品。線程總共啟動時間設置為0 表示并發,隨著線程數量不斷增加,吞吐量在線程數為600 時為987.8/sec。線程數為600 時的聚合報告如圖4 所示。之后,隨線程數增加,吞吐量開始逐漸降低并且平均響應時間變長,當線程數逐漸增大至650 時,吞吐量為866.4/sec。 可以看出系統進行商品秒殺操作能支持的最大的用戶數為600。

圖4 線程數為600 時的聚合報告Figure 4 Aggregate report with 600 threads
另外,在系統的限時搶購功能中,針對高并發方案對應程序進行相應的注釋和修改后,獲得了功能實現的常規方案程序。對不應用本文介紹的高并發案的常規方案實現的功能程序進行測試發現,在多個用戶同時秒殺同一件商品時,當線程數逐漸增大至300 時,吞吐量為875.5/sec。 之后隨線程數增加吞吐量逐漸降低,同時平均響應時間變長。 限時搶購功能性能測試如表1 所示。
本系統測試時,秒殺系統運行在普通性能的筆記本電腦上,當程序按常規方案實現時,系統的高性能并發承載量最高為300 個用戶。在應用了高并發技術方案后,系統的高性能并發承載量最高可達600 個用戶。 這說明此方案從軟件實現的角度大大提升了Web 應用系統的并發性和吞吐量, 同時保證了系統的可用性。
本文研究了高并發的優化技術,提出了基于限流、緩存、消息隊列等技術結合的方案,設計實現了商品秒殺系統。該系統中的限時搶購功能通過應用高并發技術方案,大大改善了系統在較高并發訪問時的并發性和吞吐量,即使在大量用戶進行訪問的情況下也不易出現系統響應時間過長、系統崩潰和癱瘓等現象,能夠保證用戶有較好的應用體驗。 由于本系統的實現和測試是在普通性能筆記本做為服務器的條件下完成的, 今后還可以從服務器性能、數據庫軟件性能、網絡操作等方面進行進一步優化。

表1 限時搶購功能性能測試表Table 1 Time-limited snap-up function performance test table