汪琳
(中國人民銀行南京分行,南京210004)
中間件是一種運行在操作系統、網絡和數據庫管理系統之上,應用軟件之下的軟件,其作用是為上層應用軟件提供運行支持和開發環境,幫助用戶靈活、高效地進行開發和集成[1]。中間件依據提供服務的不同,可分為消息中間件、交易中間件、對象中間件、數據訪問中間件以及安全中間件等[2]。隨著信息化的快速發展,消息中間件逐漸成為用戶的應用重點,在各類系統中隨處可見其身影[3]。傳統的消息推送中間件基于生產者-消費者模式[4],即發布方首先通過網絡傳輸把數據寄存到消息隊列中,等待訂閱方從消息隊列中獲取數據,當隊列中的數據確認被訂閱方獲取后,消息隊列中的該數據就會被移除。這里,隊列是數據傳輸的“緩沖區”,使用隊列能實現交換數據的臨時寄存,支持數據的即時或延時讀取,從而實現消息的同步或異步傳輸[5-6]。
隨著互聯網的發展,各行各業使用消息中間件進行數據交換越發普遍,這對于深度依賴消息中間件的軟件產品而言,一旦某次消息推送服務出現異常,在第一時間精準定位問題并分析異常產生的原因是至關重要的。因此,對每一次推送的“消息”進行有效的管理和監控十分必要。
目前應用比較廣泛的消息中間件有Apache的ActiveMQ、IBM的 RocketMQ、BEA的 Tuxedo等,這些消息中間件都有它們自身的特點,如在性能、安全性、健壯性、穩定性上都比較突出,然而這些消息中間件卻存在著無法快速定位異常信息、難以快速檢索某次服務信息以及難以兼容消息通訊雙方使用的不同通訊協議等問題。針對上述問題,本文基于Spring、Hibernate、Dubbo框架以及MySQL和Redis數據庫,并以與發送方通訊協議異構的第三方短信推送接口為例進行整合,設計了一個消息中間件原型。
總體架構主要分為:控制層、業務層和數據訪問層,如圖1所示。
控制層主要通過兩種架構實現:
(1)Spring MVC 架構。通過 Bootstrap、jQuery、HT?ML等前端技術實現的Web頁面和用戶進行交互。用戶通過Web頁面發出HTTP的請求首先被Spring Shiro框架實現的權限校驗攔截器攔截,對配置文件以及數據庫中用戶信息進行權限驗證,如限驗證通過,則把HTTP請求發送給控制層Controller進行處理,調用相應的業務邏輯層代碼,最后返回結果給前端Web頁面。
(2)Dubbo架構。外部系統通過遠程調用的方式訪問控制層,首先被Dubbo核心攔截,Dubbo核心通過匹配查找,找到相應的控制層類和方法并進行調用。控制層被調用后會對傳參的有效性進行校驗,校驗通過后,控制層調用業務邏輯層生成本次遠程調用服務的唯一標識號,并作為參數繼續調用相應業務的業務邏輯層代碼,最終控制層會把服務的簡單描述結果以及唯一標識號返回給外部系統。
業務邏輯層負責實現具體業務。包括生成每次遠程調用服務唯一標識號、生成調用參數的SHA1簽名、根據參數SHA1簽名查詢是否重復調用服務、第三方消息推送業務接口調用的具體邏輯,以及Web頁面檢索服務結果、管理推送服務的相關邏輯等。
數據訪問層使用Hibernate框架將第三方業務接口將成功調用的結果持久化到數據庫中,而將調用過程中出現的異常信息輸出到日志文件。對于訪問比較頻繁、數據時效性髙的熱點數據,為加快數據的訪問速度,該層使用Java API對Redis緩存數據庫中數據進行存取。

圖1 系統整體架構示意圖
對于消息推送服務中間件而言,能否兼容收、發端通訊協議不一致的情況是衡量中間件適用性的重要方面。本文設計的中間件發送端使用的是Dubbo框架集成的通訊協議,通過遠程調用使用信息推送服務中間件服務,接收端選用一個通訊協議與發送端不一致的第三方短信推送接口。該第三方短信接口可以根據請求參數的內容,動態改變短信模版內容,然后發送給具體手機號碼。具體方法為:首先需要在平臺上預置短信模版,并賦予每個模板一個模版號,將來作為請求參數,告知服務器此次服務選擇的短信模版。短信模版可以設計為:“驗證碼${code},您正在進行${product}身份驗證,打死也不要告訴別人哦!”,其中${code}、${product}為模板可變參數,接口調用者在調用短信接口時,傳入不同的替換參數即可實現發送短信內容的動態變化。
該第三方短信接口明確要求的請求發送參數為:
(1)PhoneNumbers:短信接收號碼,支持以逗號分隔的形式進行批量調用,批量調用相對于單條調用在及時性方面稍有延遲,驗證碼類型的短信推薦使用單條調用的方式。
(2)TemplateCode:短信模板號,傳入的模板必須是在第三方平臺中預置的可用模板。
(3)TemplateParam:短信模板變量,傳參規則為{“key”:”value”}鍵-值對,key的名字須和申請模板中的變量名保持一致,多個變量之間以逗號隔開。如:針對模板“驗證碼${code},您正在進行${product}身份驗證,打死也不要告訴別人哦!”,傳參時需傳入{"code":"1234","product":"alidayu"}
第三方短信接口返回的響應參數為:
(1)RequestId:請求 ID。
(2)Code:返回結果。
(3)Message:狀態碼的描述。
(4)BizId:發送回執ID,可根據該ID可查詢具體的發送狀態。
在信息推送服務中間件的服務周期中,不能保證每次服務都能正常執行,當出現異常時,信息推送服務中間件的使用者重點關心的是異常描述,而在大量的日志記錄中,開發者想要快速定位到對應本次服務的異常信息,則需要給每一個異常信息加入一個可返回給調用端的唯一標識號。如果消息推送成功完成,用戶若要從數據庫中快速檢索服務結果,也需要加入唯一的標識號。唯一標識號將與本次服務相關聯,并加入到本次服務產生的異常信息或服務結果中,最終返回給外部系統以供查詢。
唯一標識號的生成需要解決兩個問題:
(1)在多線程髙并發環境中,避免生成同樣的標識號。
(2)在信息推送服務中間件集群部署運行中,避免生成同樣的標識號。
對于第一個問題,可使用Java原子類實現,Java原子類實現了其變量的原子性,其提供的方法使用鎖機制保證其運算的原子性。在每次的服務中,都對原子類變量進行累加1,即可保證單機環境下,每次服務的唯一標識號都不一樣。
對于第二個問題,可利用數據庫系統(如MySQL)的自增id的特性,即在數據庫創建一個記錄表,該表記錄了信息推送服務中間件每次啟動后,首次提供服務的時間,該表中存在著一個自增的id列,將該id將作為信息推送服務中間件的唯一標識。
為同時解決上述2個問題,可將數據庫自增id和原子類對象值進行組合作為最終的唯一標識號,組合規則可規定為:自增id為前綴,中間拼接下劃線字符,并以原子類對象值為后綴。
通過在信息推送服務中間件中集成第三方提供的服務,一方面可以避免外部調用系統受到代碼入侵的影響,另一方面則能夠實現對服務信息的集中式管理,支持對服務信息的快速檢索。短信推送服務的業務流程示意如圖2所示。
在信息推送服務中間件短信遠程調用接口(控制層)被調用后,控制層會先行對傳入參數進行檢查,然后控制層會調用業務邏輯層流水號(唯一標識號)生成方法進行流水號的生成,再進行業務邏輯層短信發送方法調用,業務邏輯層若出現異常則通過日志服務把帶有流水號的異常信息輸出到日志文件,以方便開發和維護人員進行檢索,若服務正常,則業務邏輯層通過調用數據處理層進行對服務結果信息持久化到數據庫中。

圖2 短信推送服務的業務流程
通過在中間件中集成第三方的消息推送服務,外部系統只需要統一使用Dubbo通訊協議接入中間件,即可實現對異構通訊協議的第三方消息推送服務的使用,避免了外部系統代碼中耦合大量第三方消息推送服務的調用邏輯,且外部系統無需關心推送服務結果的持久化實現,將第三方消息推送服務集中于中間件實現,大幅度降低了對第三方消息推送服務功能的維護難度。
該消息推送服務中間件使用了遠程調用功能提供消息推送服務,由于當前的遠程調用功能僅支持同步回調,因而在整個信息推送服務過程中,調用第三方短信推送服務的時間耗占比會比較髙,若能實現遠程調用服務的異步回調,將能大大地縮減信息推送服務的響應時間,提高服務響應速度,因此,下一步將會研究遠程調用服務異步回調的實現,進一步提升消息推送服務中間件的服務性能。