?

基于微服務架構的ETC數據層平臺的設計與實現

2021-07-16 08:02
計算機應用與軟件 2021年7期
關鍵詞:隊列進程架構

于 曼 黃 凱 張 翔

(北京速通科技有限公司軟件研發部門 北京 100000)

0 引 言

伴隨著中國高速公路投資規模的不斷擴大、建設里程的不斷增加,高速公路管理所需的通信、監控和收費系統需求量在不斷擴大。電子不停車收費(Electronic Toll Collection,ETC)技術是伴隨著車輛增多和道路使用率提高而產生的一種旨在提高車輛通行能力的收費技術,適用于高速公路、交通繁忙的橋梁隧道或停車場等環境。除中國外,在美國、歐洲和日本等許多國家和地區,ETC技術也已經被廣泛應用,并形成規模效益。作為高速公路聯網電子不停車收費示范工程項目,北京市高速公路電子不停車收費系統是實現車輛不停車支付通行費功能的全自動收費系統,從2007年起已經平穩運行了十多年,至今已發展了400多萬用戶[1-4]。

近年來,北京ETC系統在高速公路、停車場和服務區收費中的應用已經開展了商業示范。取消省間收費站和用戶自主辦理、安裝、激活ETC卡和標簽等業務的出現進一步推動了用戶量和業務量的快速增長。然而,隨著業務的快速發展和用戶量的不斷增加,現有的ETC系統逐漸暴露出擴展性差、開發周期長、部署維護風險高等問題。通過對ETC系統的改造,解決原系統存在的嚴重問題是本文的最終研究目的。

1 關于ETC系統改造的分析

為了實現高速公路電子不停車收費的功能,ETC系統包括了發行系統、多路充值系統、傳輸記賬系統、銀行文件處理前置系統和清分系統等多個分支系統,其中發行系統是其核心部分,它是處理卡、電子標簽發行和充值等相關業務的系統。系統的邏輯架構如圖1所示。

圖1 ETC系統邏輯架構

由于項目設計之初規模較小,結構簡單,用戶群體小及實時通信要求低等特點,各個子系統采用傳統的單體架構模式設計,從而導致系統具有以下問題[5-7]。

(1) 代碼耦合性高。隨著業務量的不斷提升,代碼變得越發龐大,業務擴展變得愈發困難,對新需求的響應速度變慢,開發成本變高。

(2) 代碼復用性低。存在大量重復開發的代碼,各子系統間對數據庫的操作相互獨立,不支持代碼復用。

(3) 每個子系統都可以直接訪問核心數據,存在搶占數據庫資源的現象。

(4) 系統整體進行統一打包及部署,不支持熱更新,部署維護風險較高。

針對ETC系統中存在的上述問題,需在不影響系統正常運行的情況下,對其進行改造。本文搭建數據層平臺,提出各子系統中與數據操作相關的程序,將其設計為獨立的服務組件,解耦數據庫操作層與業務服務層。改造后的架構如圖2所示。

圖2 改造后ETC系統架構

數據層平臺的搭建可增強數據操作相關程序的復用性,增加可支持的數據庫類型,且不局限于某些特定數據庫。此外,數據庫訪問層的變更不會影響業務層邏輯的代碼,最大程度減小部署和運維的風險。同時,新增負載均衡提升系統吞吐量和交易處理能力,可支持千萬級的ETC用戶量和與日俱增的交易量[8]。

2 微服務架構介紹

傳統單體架構較適合簡單的輕量級應用,不適用于復雜度高、業務需求量大的中、大型應用,因此,它已不再適用ETC數據層平臺的開發。微服務架構理念的興起,很好地解決了這一問題,微服務架構對復雜企業級應用的敏捷開發部署中存在巨大優勢。

微服務指的是圍繞業務構建,協同工作的小而自治的服務,具有高的內聚性、自治性和擴展性。微服務架構的基本思想是將傳統的單體架構應用按照業務功能的不同拆分為一系列可以獨立設計、開發、部署、運維的服務單元,服務間可通過RPC或API等方式進行通信或被調用[9-12]。API網關可以對前端用戶身份進行認證和鑒別,并將前端發來的 HTTP請求轉發至對應的服務中進行處理。其架構如圖3所示[13]。

圖3 微服務體系架構

微服務架構較傳統單體架構更有利于應用的持續性開發。微服務系統中每個服務的更新和部署都獨立于其他服務組件,大大降低了系統更新的風險。每個服務單元可應用不同的編程語言開發,并可以用不同的數據存儲技術,也可由不同的開發團隊管理,當有新技術出現時,可按照模塊逐個升級,具有較好的靈活性。同時,技術的多元化使得每個服務可根據需求和行業發展狀況選擇適合的技術類型,并根據需求提供接口服務。服務間的獨立性和松耦合,實現了單個微服務出現問題不會影響系統其他服務運行的目標,容錯能力強大[14-21]。

3 數據層平臺總體架構設計

ETC數據層平臺基于微服務架構的設計理念,封裝低粒度的數據庫操作行為,為業務層提供數據操作服務。數據層平臺主要包括通信管理層、工作處理層、主控管理層及數據庫,其整體架構如圖4所示。

圖4 ETC數據層平臺結構

通信管理層基于IO復用原則,負責外圍系統請求的接入接出。當請求接入成功建立連接后,通信管理層采用多線程技術獲取并解析請求, 根據業務類型的不同分配給對應的工作進程處理。消息隊列機制與線程鎖在保證線程安全的前提下,借助共享內存完成消息的傳遞、并行處理并轉發。多線程能夠合理高效利用服務器資源,可大幅度提升消息處理能力。在請求高峰期,消息隊列可以有效緩解壓力,避免因處理能力不足造成服務不可用等情況。共享內存作為通信模塊與工作模塊的橋梁,是目前效率最高的進程間通信技術, 所有交互完全在內存中完成且可自定義分配內存塊大小,在合理設計數據結構的前提下具備了很高的適用性,并且Linux系統提供相關底層函數,保證了平臺使用時的穩定性與安全性。

工作處理層是由多個獨立進程實現,根據通信管理層轉發來的請求調用動態庫微服務,處理相關業務邏輯, 最后將處理結果回發至通信管理層。工作處理層將業務邏輯根據不同的功能拆分成不同的子模塊,將各個模塊封裝為相互獨立的動態庫,依靠自身平臺框架用外圍系統請求參數實例化動態庫中的服務,從而基于C++實現依賴注入控制反轉的目的。該實現方式的本質類似于微服務架構,動態庫中的微服務變更無須編譯平臺代碼,服務之間互不影響,極大地降低了平臺和服務的耦合性,提高了平臺和服務的可擴展性,使得系統整體結構變得非常靈活,是目前C++框架所不具備的[21-23]。

主控管理層主要負責管理通信管理層與工作處理層以及監控服務的啟動。當請求開始處理時,主控管理層負責監控服務處理的時長,根據特定服務類型及其自定義超時時間判定超時后的處理方法,減少調用等待時長,提升響應速度。同時主控管理層監控通信進程與工作進程存活狀態,當意外掛掉進程時會重新啟動,確保數據層平臺的穩定性與安全性。

4 數據層平臺關鍵技術

4.1 C++實現服務注入

一般C++框架中,如需要在實體A中使用實體B,會直接在實體A中創建實體B的對象,換而言之,是在實體A中去主動獲取所需要的外部資源實體B,如圖5所示。但若應用反向注入控制反轉的方式,實體A如需使用實體B,無須主動創建實體B,而是被動地等待,等待容器獲取一個B的實例,然后容器反向把實體B的實例注入到實體A中。這種在Java/.Net中是依靠IOC/DI的容器來實現,通過配置文件中的類名實例化業務類進行依賴注入的。

圖5 實體A應用實體B的傳統方式

ETC數據層平臺是一種通過C++實現IOC(控制反轉)模式,服務和系統松耦合,進程分發靈活的平臺系統。平臺將根據不同功能拆分成的各個模塊封裝為相互獨立的動態庫,依靠請求參數實例化動態庫中的服務,將可用動態庫服務注入到自定義容器中,從而達到依賴注入控制反轉的目的,如圖6所示。在調用服務時,從自定義容器中取出對象進行后續邏輯處理,可徹底切分開服務與平臺間的耦合,使后期服務變動無需編譯平臺,平臺編譯無需編譯服務。

圖6 C++實現服務的依賴注入

4.2 負載均衡

系統的負載均衡器主要對分發服務的任務處理請求選擇最優節點。該系統負載均衡的均衡策略為處理請求首先都暫存到阻塞隊列中等待策略模塊過濾轉發,分發服務的數據處理請求,負載均衡器主要根據CPU占用率等進行過濾選擇。當選擇到最優的數據分析節點則將請求發送到對應的節點進行處理,而如果有多個符合條件的節點,則負載均衡器將隨機選擇其中一個轉發處理請求[24]。

4.3 redis集群化

redis集群化基本原理是通過在多臺服務器上部署redis, 每臺服務器上啟動多個redis節點, 利用keepalive做虛擬IP實現訪問統一與高可用。其目的是在單臺服務器故障時可自動切換并短信報警,利用ruby語言實現多節點數據互通與故障切換功能解決單點故障問題。

在基于上述高可用情況下,數據平臺根據不同流水號分類成不同的key在redis中建立多個list序列, 根據查詢數據庫中最大值進行一定數量的自增, 自增后批量插入與流水號相對應的list序列中并定時檢查redis中流水號數量進行補充。

在啟動多平臺時每個平臺統一連接redis集群, 各平臺接收到生成流水號的請求時根據不同流水號類型解析出不同key,然后利用redis acl庫的API 調用pop方法彈出與其對應的value響應給業務層 ,pop操作為原子性, 可保證數據唯一性。

4.4 7×24小時不停機

數據層平臺進程間通信基于共享內存機制是目前最快的進程間通信技術,共享內存是在linux系統級開辟的內存空間,只要服務器不重啟,開辟的內存空間就不會被釋放(手動釋放例外)?;诒緳C制可以保證進程意外崩潰時利用監控機制重啟進程后,進程根據配置文件計算得出共享內存地址后依舊可以通過其地址訪問到共享內存,從而取出未處理的數據繼續進行處理。理論上管理進程不崩潰,數據層平臺可不間斷運行并隨時可以手動kill除管理進程外的任意進程。

基于流水號去中心化,共享內存持久化的技術架構下,結合Nginx進行多臺負載可保證平臺7×24小時不間斷運行并隨時可進行單臺服務器的數據平臺更新與維護工作,不會對線上業務產生任何影響。

此外,程序后臺啟動、切換用戶、切換shell、關閉shell等操作不會對啟動的進程造成影響,數據層平臺所有進程都采用守護進程方式運行。除手動觸發關閉外,系統不重啟將一直運行。

運行期間,數據平臺支持服務的熱更新,不停止當前業務的前提下對動態庫服務進行更新、停止和加載等操作??紤]業務量的與日俱增, 為保證流水號的唯一性設置主機與從機并支持熱切換,在日后的使用場景中可保證接口不變的情況下自如切換主從。

4.5 服務分化

實際應用中通常讀類業務耗時長、業務復雜,并且在大量請求到達時多線程解決方案并不理想,故大多選擇隊列緩存的方式進行處理。而因為讀類業務的超時會導致隊內后續業務無法及時處理,最終導致連接超時,業務執行失敗或因阻塞導致更新不及時使處理邏輯混亂等問題,如充值、動賬等核心業務,都可能因為讀類業務的阻塞導致請求方認為該次請求無效而重復請求,最終導致錯賬等問題的出現。

服務分化主要是根據其對數據庫操作,將服務劃分成讀與寫的類型?,F階段對數據庫的查詢操作劃分為讀類業務,對數據庫的其他操作劃分為寫類業務(如:新增、修改、刪除)。在數據庫中使用自定義表用于持久化所有業務類型信息,方便后期新增與維護,在業務服務器啟動時會將所有業務服務進行初始化,通過讀取數據庫自定義表的內容將相關啟用狀態下的業務類型加載到內存中,使后續比對都在內存中進行,提高效率。具體業務細分成1=讀、2=寫、3=其他,加載到內存后,請求到達進行初步處理放入隊列, 主線程從隊列中取出消息后,根據請求的服務號在內存中計算得到對應業務類型,根據業務類型(讀/寫)分發到對應的隊列中,后續工作進程會從隊列中取出請求進行處理并響應。

服務分化成功地將讀類業務與寫類業務區分開來,互不影響,在實際應用中讀類業務占到很大比例,通過分離的方式可以更具針對性地對讀類業務進行優化,同時也解決了因讀類業務的阻塞導致其他后續業務無法及時處理的問題。

4.6 服務監控

平臺的管理進程模式,可監控通信進程與工作進程存活狀態,監控服務調用時長。根據自定義時間判定超時服務后處理,目前生產環境是調用服務前記錄時間管理進程監控。如果15 s后沒有改變,則認為該服務超時,自動殺掉其工作進程,從而減少業務邏輯層等待時長,提升響應速度。

系統運行期間可控制日志輸出級別,包括調試模式(所有日志都輸出)、警告模式(只輸出警告和錯誤日志)和錯誤模式(只輸出錯誤日志)等日志級別??赏ㄟ^更改配置文件靈活控制日志級別,對于問題服務可通過日志快速準確定位問題。此外,利用Redis轉存日志、異步打印日志、減少輸入輸出等待時間來提升整體平臺的處理能力,在redis不可用時可自主進行降級到本地輸入輸出打印,保證日志信息不丟失。

5 系統實現

原ETC系統的歷史數據主要存儲在DB2和mongo兩種類型的數據庫中,因此ETC數據層平臺在開發實踐中,為了最大程度減小數據遷移的風險,同時滿足業務需要,依然應用DB2和mongo兩種類型的數據庫。在進行平臺和服務劃分時,分為DB2和mongo兩個數據層平臺,以便于根據數據庫種類和性能的不同對服務器資源等進行分配。訪問DB2數據庫的數據操作層服務搭載在DB2數據平臺上,訪問mongo數據庫的數據層服務搭載在mongo數據平臺上。數據服務搭載平臺,完成服務載入與卸載、請求接收、分發、響應生成,服務組件以動態庫的形式封裝不同的服務。

5.1 數據層服務工作流程

當一個請求由客戶端發起后, 經由前置服務器轉發至負載均衡服務后到達業務服務器,業務服務器進行邏輯判斷后向后臺發送請求,至此成功進入到新架構的服務器內部,開始正式處理及解析。服務的調用流程如圖7所示。

圖7 數據服務調用流程

1) 初始化階段。數據層平臺啟動,初始化一個下標為8位數的數組(系統協議的服務號定長為8位)并將數組內容初始化為0,這些服務號是根據業務需求從10000000遞增而來,并根據業務進行細分。如果涉及讀數據庫則將其類型標記為1,如果非讀數據庫行為則標記為2,在添加服務時已將這些信息持久化保存到數據庫內。在平臺初始化數組后, 讀取數據庫內服務類型信息,并根據服務號對其所在下標數組的值進行更改,如10000001為讀服務對應的類型值為1,則將開始初始化為0的下標號為10000001的數組值從0改為1,以此類推, 至此初始化工作已經完成,所有業務需要調用的服務都已加載到內存中并根據類型進行了細化。

2) 請求到達。一個新的請求成功轉發到數據層平臺后,平臺根據協議開始解析請求,截取服務號及消息體內容,根據解析得到的服務號可以得知請求的業務類型,也可以根據自定義的服務號來判斷是否需要進行特別的邏輯處理,而且也可以結合服務號來定制相應規則。如:讀類業務請求到達時,當前隊列如果負載超過80%則拒絕本次請求等個性化功能,如果本次服務號無特殊處理邏輯也無拒絕連接特征,則將其放入通信隊列當中,等待下一步處理。

3) 開始分發隊列。數據層平臺從通信隊列里取出一條經上一步篩選過的請求報文后,將本次請求的服務號作為數組的下標,從中取出當前服務號下標數組的值,由此確定本次請求的操作類型。如果為讀類型,則將本次請求消息體內容放入讀隊列中, 反之則放入寫隊列中。隊列的傳輸媒介考慮到效率問題,采用當前最快的進程間通信方式即共享內存來完成,在此期間可根據業務需求自定義功能。

4) 請求處理階段。從消息隊列中獲取請求,根據Key值(服務號)獲取對應服務的內存地址,調用動態庫中的服務對其進行處理,并將響應結果返回通信管理層,發送給前置。

至此數據層平臺從一個請求的發起、分發、處理、返回響應的功能已介紹完畢。

此外,系統在添加或者更新服務組件時需要人工進行干預,處理流程如圖8所示。整個過程簡單易操作。主要操作流程是服務拷貝、服務列表更新、通信進程添加啟動服務。這個過程對數據層平臺無影響,也對其他正常運行的服務無影響,實現了服務的熱更新。

圖8 數據操作層人工干預流程

5.2 數據層平臺應用優勢分析

ETC數據層平臺本質是應用C++框架實現了微服務理念,按照業務功能分化出細粒度的數據服務,同時依據現有的先進技術對框架進行不斷完善。這種設計理念使得ETC系統在程序代碼層面、系統層面、系統響應速度層面和部署維護層面展現出很大優勢。

數據層平臺改造前后性能對比如表1所示。數據服務的單獨平臺化增強了與數據庫交互代碼的復用性,同時實現了系統間的代碼復用,可避免大量重復代碼的出現。細粒度的服務更加便于功能的新增與修改,增加了系統的擴展性。數據層平臺對前置系統的編程語言和架構技術沒有要求,同時其可以與多種類型數據庫靈活交互,并且可支持多種通信協議,平臺接口相對靈活,具有很高的兼容性,增強了平臺的可用性。負載均衡、消息隊列、多線程和共享內存技術的使用很好地提高了通信速度,實踐證明數據服務的響應速度較改造前系統可提高至微秒級以內。同時,數據層平臺支持服務級別的熱更新,進行不停機維護,服務新增或更新不會影響業務層邏輯代碼,對其他服務以及系統的整體運行也無任何影響。此外,相較改造前被動地處理投訴方式修改生產bug,數據層平臺可自主監控平臺問題,并將監控結果記錄,進行自查自修。

表1 數據層平臺改造前后性能對比表

數據層平臺是公司內部自主研發,相較于開源系統或委托給第三方開發的系統,自主研發的系統平臺更具安全性以及應變靈活性,系統后期升級、優化的靈活性也更高,能夠快速且準確地響應公司實際需求,可追蹤服務狀態記錄, 進行自定義處理。

6 結 語

本文主要目的和創新是根據北京ETC系統的實際情況,基于微服務架構思想,利用C++框架搭建與數據庫交互的數據層平臺,實現對北京ETC系統的初步改造,從而解決原系統中的問題。數據層平臺用動態庫形式封裝了微服務,利用C++框架實現了服務的依賴注入控制反轉。數據層平臺具有微服務框架的特點,同時是自主研發的框架,在新技術的應用上具有很靈活的選擇權。數據層平臺的搭建為ETC系統整體改造提供了條件,其松耦合的特點,更好地支持在傳統架構下的業務邏輯層的代碼重構。因此,下一步將重點考慮ETC系統業務邏輯部分的改造。

猜你喜歡
隊列進程架構
功能架構在電子電氣架構開發中的應用和實踐
基于車車通訊的隊列自動跟馳橫向耦合模型
隊列隊形體育教案
構建富有活力和效率的社會治理架構
Dalvik虛擬機進程模型研究
快速殺掉頑固進程
不留死角 全方位監控系統
青春的頭屑
中外民主法制進程專題復習
VIE:從何而來,去向何方
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合