孫 立 哲
(公安部第三研究所 上海 201204)
前期在異步接口性能測試方面做了一些初步性探索工作,并以HTTP異步接口為例設計一種能夠覆蓋異步接口內(nèi)部完整業(yè)務流程的性能測試方案[1]。該性能測試方案的核心思想是在待測異步接口中增加將請求數(shù)據(jù)和異步響應數(shù)據(jù)分別寫入不同數(shù)據(jù)庫表的處理邏輯,并在壓測結(jié)束后通過對數(shù)據(jù)庫表中請求數(shù)據(jù)和響應數(shù)據(jù)作比對統(tǒng)計分析,以評估異步接口完整業(yè)務下的性能情況。該方案覆蓋了異步接口內(nèi)部完整業(yè)務處理流程,能對整體性能表現(xiàn)作出較為全面的評測。但尚未對方案中所涉及的關(guān)鍵技術(shù)以及這些技術(shù)實現(xiàn)是否會對待測異步接口原有性能表現(xiàn)產(chǎn)生影響等作較為深入的設計優(yōu)化研究與實踐驗證。這些關(guān)鍵技術(shù)主要表現(xiàn)在兩方面,一是異步接口中增加的數(shù)據(jù)入庫模塊;二是對庫中數(shù)據(jù)作快速統(tǒng)計分析的模塊。本文重點針對這些技術(shù)及其影響做進一步的技術(shù)探索與實踐驗證。
采用Hibernate框架對象持久化技術(shù)[2]、EJB組件封裝與依賴注入[3]、線程池與多線程并發(fā)技術(shù)[4-5]來實現(xiàn)將數(shù)據(jù)寫入數(shù)據(jù)庫。
首先,以Hibernate框架對象持久化技術(shù)實現(xiàn)對Java對象數(shù)據(jù)與數(shù)據(jù)庫表字段映射,以及與數(shù)據(jù)庫之間的連接與數(shù)據(jù)訪問操作執(zhí)行。Hibernate是一個開源的對象關(guān)系映射框架,它實現(xiàn)了對數(shù)據(jù)庫連接輕量級的對象封裝,能提供高性能的對象關(guān)系型持久化存儲和查詢服務[6]。
其次,采用EJB組件封裝技術(shù)將對數(shù)據(jù)庫的增刪改查等操作封裝成接口,通過調(diào)用EJB組件接口觸發(fā)對數(shù)據(jù)庫的操作。采用EJB依賴注入技術(shù)將已封裝好的接口注入異步接口內(nèi),異步接口內(nèi)以直接調(diào)用EJB接口的方式完成對數(shù)據(jù)庫表數(shù)據(jù)的寫入。EJB是服務端組件模型,設計目標與核心應用是部署分布式應用程序。EJB容器(如JBoss)提供了對象池和緩存機制,沒有事務機制的無狀態(tài)Session Bean比普通JavaBeans具有更強的性能。
采用線程池,根據(jù)實際需求創(chuàng)建一定數(shù)量的線程數(shù),把向數(shù)據(jù)庫寫數(shù)據(jù)的操作放入線程池以多線程的方式去執(zhí)行,降低數(shù)據(jù)庫操作對異步接口自身性能的影響。在高并發(fā)場景下,可通過調(diào)整線程數(shù)來調(diào)整性能表現(xiàn)。線程池及各線程在異步接口服務程序部署啟動時創(chuàng)建。異步接口將寫數(shù)據(jù)的任務傳給線程池時,線程池將任務分配給一個線程來執(zhí)行。任務執(zhí)行結(jié)束后,該線程返回線程池中成為空閑狀態(tài),等待執(zhí)行下一個任務。線程池多線程處理機制具有很好的性能優(yōu)勢。
與用于記錄數(shù)據(jù)的各數(shù)據(jù)庫表對應,創(chuàng)建實體類。采用JPA對象持久化技術(shù)建立實體類與數(shù)據(jù)庫表的映射關(guān)系。采用EntityManager接口實現(xiàn)對Java對象到數(shù)據(jù)庫表的寫入操作。EntityManager是JPA中用于對數(shù)據(jù)庫表數(shù)據(jù)進行增刪改查的接口,連接內(nèi)存中的Java對象和數(shù)據(jù)庫的數(shù)據(jù)存儲[7]。采用EntityManager的persist方法和flush方法將實體類對象持久化寫入數(shù)據(jù)庫表。用EJB本地接口封裝對數(shù)據(jù)庫表的操作。以EJB依賴注入的方式將EJB接口實例注入異步接口,異步接口內(nèi)調(diào)用EJB接口實例的各方法來完成對數(shù)據(jù)庫表的操作。采用Java開發(fā)工具包中Executors類的newFixedThreadPool方法創(chuàng)建線程池。將寫入數(shù)據(jù)庫的操作單獨封裝為一個數(shù)據(jù)庫寫入方法。在異步接口實現(xiàn)類中新建內(nèi)部類并實現(xiàn)Runnable接口,在覆寫的run方法中調(diào)用外部類的數(shù)據(jù)庫寫入方法,以新建內(nèi)部類對象的方式新建線程并放入線程池。以一級異步接口為例,寫數(shù)據(jù)入對應數(shù)據(jù)庫表的類圖如圖1所示。
圖1 一級異步接口寫數(shù)據(jù)入數(shù)據(jù)庫表類圖
以一級異步接口為例,具體實現(xiàn):
1) 定義實體類,實現(xiàn)持久化。與請求表和響應表相對應,創(chuàng)建兩個實現(xiàn)序列化接口的實體類,記為請求實體類和響應實體類。兩個實體類中定義各屬性,分別對應表中各字段,并在各字段前添加相應注釋,如唯一自增注釋。創(chuàng)建兩個普通類,記為請求類和響應類,分別對應請求實體類和響應實體類,普通類中屬性分別對應實體類中除唯一自增屬性之外的其他各屬性。
2) 定義EJB接口,用于封裝將實體類數(shù)據(jù)持久化寫入數(shù)據(jù)庫的操作。創(chuàng)建兩個EJB接口,記為請求接口和響應接口,接口中聲明數(shù)據(jù)寫入方法,方法入?yún)㈩愋头謩e為請求類類型和響應類類型。創(chuàng)建兩個EJB無狀態(tài)Session Bean類,記為EJB請求類和EJB響應類,分別實現(xiàn)請求接口和響應接口,類前加無狀態(tài)注釋,加PersistenceContext依賴注入EntityManager實例,PersistenceContext依賴中的“持久化單元名”與配置文件persistent.xml中定義的持久化單元persistence-unit的name屬性值一致。在ds.xml文件中配置MySQL數(shù)據(jù)源,然后在persistent.xml文件中引入該數(shù)據(jù)源。ds.xml文件與異步接口部署在相同目錄下。實現(xiàn)接口中聲明的數(shù)據(jù)寫入方法,方法中將入?yún)㈩悓ο笞鲾?shù)據(jù)轉(zhuǎn)換轉(zhuǎn)為對應的實體類對象,調(diào)用EntityManager實例的persist方法和flush方法將實體類對象數(shù)據(jù)寫入數(shù)據(jù)庫表。
3) 在異步接口中加依賴注入EJB接口實例,并以線程池多線程方式調(diào)用EJB接口實例的數(shù)據(jù)庫寫入方法。在異步接口中依賴方式注入EJB實例時,異步接口需設為EJB組件接口。在異步接口中加EJB注釋引入EJB請求類實例和EJB響應類實例。創(chuàng)建.properties屬性配置文件,在配置文件中設定線程池線程數(shù)。創(chuàng)建線程池工具類,類中讀取配置文件獲取線程數(shù),并在類加載時自動創(chuàng)建靜態(tài)線程池及各線程。在異步接口實現(xiàn)類中,將調(diào)用EJB接口實例方法進行數(shù)據(jù)庫寫入的操作單獨封裝為一個數(shù)據(jù)庫寫入方法,方法內(nèi)調(diào)用EJB請求類實例對象的數(shù)據(jù)寫入方法和EJB響應類實例對象的數(shù)據(jù)寫入方法。在異步接口實現(xiàn)類中新建內(nèi)部類并實現(xiàn)Runnable接口,在覆寫的run方法中調(diào)用外部類的數(shù)據(jù)庫寫入方法。在異步接口內(nèi)部作異步響應返回前,獲取線程池工具類中預創(chuàng)建的靜態(tài)線程池,以新建內(nèi)部類對象的方式新建線程并放入線程池中執(zhí)行。
基于以上設計與實現(xiàn),以HTTP異步接口為例,對異步接口分將數(shù)據(jù)寫入數(shù)據(jù)庫和不寫數(shù)據(jù)庫兩種場景作性能對比驗證測試。測試環(huán)境整體架構(gòu)如圖2所示。
圖2 性能測試環(huán)境整體架構(gòu)
異步接口請求數(shù)據(jù)與響應數(shù)據(jù)均采用序列化的JSON(JavaScript Object Notation)字符串格式。請求數(shù)據(jù)串中包含業(yè)務請求時間、業(yè)務流水號、其他特定業(yè)務數(shù)據(jù)鍵值對。異步響應數(shù)據(jù)串中包含異步響應時間、業(yè)務流水號、業(yè)務處理結(jié)果、其他特定業(yè)務處理結(jié)果數(shù)據(jù)鍵值對。響應串中業(yè)務流水號與對應的請求串中業(yè)務流水號一致。請求數(shù)據(jù)表中每一條請求串都能在響應數(shù)據(jù)表中找到唯一響應數(shù)據(jù)記錄,說明所有請求均被成功響應。
驗證測試時,請求數(shù)據(jù)業(yè)務請求時間取壓測客戶端當前系統(tǒng)時間并精確到毫秒級。采用隨機生成通用唯一標識作為業(yè)務流水號,確保每個請求具有唯一性業(yè)務流水號。響應數(shù)據(jù)業(yè)務響應時間取服務端當前系統(tǒng)時間。服務端系統(tǒng)時間與壓測客戶端系統(tǒng)時間同步。異步接口內(nèi)每個響應數(shù)據(jù)的業(yè)務流水號取其對應請求數(shù)據(jù)的業(yè)務流水號。請求數(shù)據(jù)與響應數(shù)據(jù)中其他特定業(yè)務數(shù)據(jù)采用固定數(shù)據(jù)。
壓測過程中按上述格式及方式準備請求數(shù)據(jù)串請求異步接口。
驗證環(huán)境為:
1) 數(shù)據(jù)庫所在服務器硬件配置為8核處理器、16 GB內(nèi)存、300 GB硬盤。
2) 異步接口部署所在服務器硬件配置為8核處理器、16 GB內(nèi)存、300 GB硬盤。
3) 異步回調(diào)接口部署所在服務器硬件配置為4核處理器、8 GB內(nèi)存、300 GB硬盤。
4) JMeter所在壓測客戶機硬件配置為8核處理器、16 GB內(nèi)存、200 GB硬盤。
5) 整套環(huán)境通過一個千兆以太網(wǎng)交換機部署在同一局域網(wǎng)內(nèi)。
驗證結(jié)果如下:
在并發(fā)數(shù)相同以及客戶端和服務端處理器、內(nèi)存等系統(tǒng)資源未出現(xiàn)資源耗盡等性能瓶頸情況下,客戶端壓測并發(fā)數(shù)和服務端線程池線程數(shù)配置均為70,不寫數(shù)據(jù)庫時異步接口每秒處理請求數(shù)2 824.8次,寫數(shù)據(jù)庫時異步接口每秒處理請求數(shù)2 473.9次。對比結(jié)果顯示,寫數(shù)據(jù)庫時異步接口性能有略微下降。但相比而言,增加寫數(shù)據(jù)庫處理邏輯對異步接口原有性能產(chǎn)生的影響不大。
前期設計實現(xiàn)的數(shù)據(jù)統(tǒng)計分析程序可以對請求數(shù)據(jù)與響應數(shù)據(jù)作出正確的統(tǒng)計分析。但因采用的是單線程處理,且數(shù)據(jù)庫表未創(chuàng)建索引,在數(shù)據(jù)量較大的情況下,統(tǒng)計分析執(zhí)行過程耗時會比較長。為了提高統(tǒng)計分析執(zhí)行速率,采用數(shù)據(jù)庫表索引、線程池和多線程并發(fā)技術(shù)作進一步的設計優(yōu)化。索引的合理創(chuàng)建可提高數(shù)據(jù)庫表數(shù)據(jù)的查詢檢索速度[8],線程池多線程并發(fā)技術(shù)可實現(xiàn)對數(shù)據(jù)分組作并行查詢比對,從而提高統(tǒng)計分析執(zhí)行速率。
數(shù)據(jù)統(tǒng)計分析過程中,對請求表作數(shù)據(jù)查詢時的檢索條件是唯一屬性字段。唯一屬性字段本身是一種特殊的主鍵索引[9],因此數(shù)據(jù)檢索速度會比較快。對響應表作數(shù)據(jù)查詢時的檢索條件為業(yè)務流水號,業(yè)務流水號默認不是主鍵,也沒有對應的索引,因此,對響應表的數(shù)據(jù)查詢速度會比較慢。故對響應表增加業(yè)務流水號字段索引,以提高表數(shù)據(jù)檢索速度。
在統(tǒng)計分析過程中,數(shù)據(jù)比對是從請求表中根據(jù)唯一屬性自增字段依序提取請求數(shù)據(jù),然后根據(jù)請求數(shù)據(jù)中業(yè)務流水號與響應表中數(shù)據(jù)作查詢并比對。請求表中不同的請求數(shù)據(jù)之間沒有關(guān)聯(lián)關(guān)系,請求表中數(shù)據(jù)與響應表中數(shù)據(jù)預期存在一一對應關(guān)系。在同一時間可以并行地在不同的線程中分別對不同的數(shù)據(jù)作查詢比對。因此,可引入線程池多線程來實現(xiàn)并行處理,從而減少統(tǒng)計分析時間。將請求數(shù)據(jù)按照唯一屬性自增字段作分組,對不同分組分別同時在不同線程進行獨立統(tǒng)計分析,并記錄分組內(nèi)統(tǒng)計分析結(jié)果。在所有線程全部執(zhí)行結(jié)束后,對各分組內(nèi)統(tǒng)計分析結(jié)果再作匯總統(tǒng)計。
創(chuàng)建.properties屬性配置文件,在文件中設定數(shù)據(jù)分組長度,分組長度表示分組內(nèi)的數(shù)據(jù)個數(shù)。統(tǒng)計分析程序讀取配置文件,獲取分組長度,將所有請求數(shù)據(jù)按分組長度作分組。若請求數(shù)據(jù)總數(shù)是分組長度的整數(shù)倍,則請求數(shù)據(jù)總數(shù)除以分組長度所得整數(shù)即為分組個數(shù)。若請求數(shù)據(jù)總數(shù)非分組長度的整數(shù)倍,則請求數(shù)據(jù)總數(shù)除以分組長度所得整數(shù)再加一即為分組個數(shù)。如果請求數(shù)據(jù)總數(shù)小于等于分組長度,則以單線程作統(tǒng)計分析,如果請求數(shù)據(jù)總數(shù)大于分組長度,則以多線程分組作統(tǒng)計分析。以分組個數(shù)作為線程數(shù),創(chuàng)建線程池。一個線程對應一個分組,單個分組內(nèi)的數(shù)據(jù)統(tǒng)計分析在一個線程中執(zhí)行,不同分組在不同線程中并行執(zhí)行。對每個分組中各請求數(shù)據(jù)根據(jù)唯一屬性自增字段值按順序逐一提取并與響應表中數(shù)據(jù)作查詢比對,分別統(tǒng)計各分組內(nèi)的請求成功數(shù)、請求失敗數(shù)、平均響應時間、最小響應耗時、最大響應耗時,并將每個分組的統(tǒng)計結(jié)果分別記錄在不同的文件內(nèi),一個分組對應一個文件。所有線程執(zhí)行結(jié)束后,對各分組統(tǒng)計分析結(jié)果作匯總集成統(tǒng)計,得出總請求成功數(shù)、總請求失敗數(shù)、總平均響應時間、總最小響應耗時、總最大響應耗時。多線程統(tǒng)計分析處理邏輯流程如圖3所示,分組統(tǒng)計子流程如圖4所示。
圖3 多線程統(tǒng)計分析處理邏輯流程
圖4 分組統(tǒng)計子流程
線程池中線程數(shù)與分組個數(shù)相同,分組個數(shù)取決于配置文件中分組長度設定值。因此,在總請求數(shù)一定的情況下,配置文件中設定的分組長度越小,線程數(shù)越多,分組長度越大,線程數(shù)越少。線程數(shù)過少,統(tǒng)計分析執(zhí)行耗時越接近單線程處理耗時,耗時會比較久。線程數(shù)過多,統(tǒng)計分析程序執(zhí)行時會過多地占用系統(tǒng)資源,系統(tǒng)資源耗盡時會影響統(tǒng)計分析程序的執(zhí)行速率。合理設定數(shù)據(jù)分組長度,才能使統(tǒng)計分析程序達到較優(yōu)的性能。
基于以上設計優(yōu)化與實現(xiàn),對數(shù)據(jù)統(tǒng)計分析程序作優(yōu)化前后的性能對比驗證測試。
統(tǒng)計分析過程中所用數(shù)據(jù)為異步接口性能壓測時分別寫入請求數(shù)據(jù)表和響應數(shù)據(jù)表中的數(shù)據(jù)。
驗證環(huán)境如下:
1) 統(tǒng)計分析程序所在客戶機硬件配置為8核處理器、16 GB內(nèi)存、200 GB硬盤。
2) 數(shù)據(jù)庫服務所在服務器硬件配置為4核處理器、8 GB內(nèi)存、60 GB硬盤。
3) 客戶機與服務器在同一局域網(wǎng)內(nèi)。
驗證結(jié)果如下:
1) 總請求數(shù)為479 400時,優(yōu)化前的單線程統(tǒng)計分析耗時為385.77 s,優(yōu)化后的多線程統(tǒng)計分析耗時為183.53 s。
2) 總請求數(shù)為741 812時,優(yōu)化前的單線程統(tǒng)計分析耗時為829.16 s,優(yōu)化后的多線程統(tǒng)計分析耗時為317.97 s。
3) 總請求數(shù)為1 531 640時,優(yōu)化前的單線程統(tǒng)計分析耗時為1 726.97 s,優(yōu)化后的多線程統(tǒng)計分析耗時為603.48 s。
對比結(jié)果顯示,優(yōu)化后的基于多線程并發(fā)技術(shù)的統(tǒng)計分析程序執(zhí)行速率提升顯著。
本文在前期進行的關(guān)于異步接口性能測試方案設計基礎上,針對方案中所涉及的一些關(guān)鍵技術(shù)點,作了進一步的設計優(yōu)化研究與實踐驗證。主要表現(xiàn)在兩方面,一方面是采用Hibernate框架JPA對象持久化技術(shù)、EJB組件封裝與依賴注入、線程池與多線程并發(fā)技術(shù),對異步接口數(shù)據(jù)寫入數(shù)據(jù)庫模塊作了優(yōu)化設計與實現(xiàn)以及驗證測試,另一方面是采用數(shù)據(jù)庫表索引、線程池與多線程并發(fā)處理等技術(shù)對數(shù)據(jù)統(tǒng)計分析程序作了優(yōu)化設計與實現(xiàn)以及對比驗證測試。驗證結(jié)果顯示,本文做優(yōu)化設計并實現(xiàn)的異步接口數(shù)據(jù)寫入數(shù)據(jù)庫的模塊性能表現(xiàn)良好,對異步接口原有性能影響較小,優(yōu)化后的數(shù)據(jù)統(tǒng)計分析程序相比優(yōu)化前在執(zhí)行速率上提升了1~2倍,性能提升較明顯。