梅巧玲,楊立鵬,樊春美,馮 炎
(中國鐵道科學研究院集團有限公司 電子計算技術(shù)研究所, 北京 100081)
在鐵路12306售票系統(tǒng)中,常用聯(lián)系人集群是售票主流程中的重要環(huán)節(jié),也是用戶能否購票成功的關(guān)鍵。常用聯(lián)系人集群應用分布式內(nèi)存數(shù)據(jù)庫技術(shù),每個集群由若干臺服務器組成,每臺服務器上數(shù)百計的內(nèi)存全部被該進程占用,存放12306售票系統(tǒng)所有用戶大約幾十億條的常用聯(lián)系人數(shù)據(jù)。數(shù)據(jù)庫為雙表結(jié)構(gòu),極大地提高了查詢效率,但由于現(xiàn)階段產(chǎn)品本身的局限性,兩個表之間的一致性無法得到保證,當常用聯(lián)系人集群持久化的文件不可用時,需要從數(shù)據(jù)庫重新導出數(shù)據(jù)再導入集群,進行創(chuàng)建索引等操作,進行一次全量數(shù)據(jù)的導入和創(chuàng)建索引要花費幾十個小時,給維護工作帶來很大困擾。
文章在常用聯(lián)系人集群原有架構(gòu)的基礎上,將GEMFIRE內(nèi)存數(shù)據(jù)庫產(chǎn)品替換為分布式內(nèi)存服務軟件Redis,新架構(gòu)不僅具備查詢效率高等優(yōu)點,還解決了數(shù)據(jù)一致性、維護便捷、故障快速恢復等問題。
常用聯(lián)系人集群新架構(gòu)如圖1所示。整個系統(tǒng)采用雙中心的模式,兩個中心共同承擔所有互聯(lián)網(wǎng)流量,形成雙活態(tài)勢并且互為應急,任何一個中心發(fā)生故障,業(yè)務均能切換到另一個中心工作,實現(xiàn)應用級容災。每個中心的常用聯(lián)系人集群主要由查詢服務和數(shù)據(jù)同步兩個功能組成,查詢服務由常用聯(lián)系人微服務集群、分布式內(nèi)存數(shù)據(jù)服務和Redis-Server組成;數(shù)據(jù)同步主要由數(shù)據(jù)庫、消息服務器Rabbit MQServer和消費端組成。
圖1 常用聯(lián)系人集群新架構(gòu)
常用聯(lián)系人微服務是由高性能Web服務Nginx和腳本語言lua組合而成的應用服務,提供聯(lián)系人操作的API查詢服務。常用聯(lián)系人微服務是無狀態(tài)的,支持水平擴展,支持集群之間的流量分配和集群切換,支持按照百分比進行多集群流量分配,能夠?qū)崟r查看各集群的使用狀態(tài)和流量配置,每一個服務進程都有對應的監(jiān)控腳本,發(fā)現(xiàn)問題時先自動重啟,重啟失敗后發(fā)送消息到報警中心。
分布式內(nèi)存數(shù)據(jù)服務是客戶端連接Redis的代理服務, 使用go語言開發(fā),支持多線程,實現(xiàn)了Redis協(xié)議,支持原生的大部分命令。對于同一個業(yè)務集群而言,可以同時部署多個分布式內(nèi)存數(shù)據(jù)服務實例,不同分布式內(nèi)存數(shù)據(jù)服務之間由運維管理平臺保證狀態(tài)同步。分布式內(nèi)存數(shù)據(jù)服務還負責數(shù)據(jù)的預先分片,獲取請求校驗命令合法后,根據(jù)操作的key值HASH到1 024個slot相應的后端Redis服務器進行執(zhí)行,分布式內(nèi)存數(shù)據(jù)服務跟后端Redis的一個數(shù)據(jù)庫TCP連接,通過pipeline模式與Redis交互。它將業(yè)務系統(tǒng)和Redis存儲剝離開,實現(xiàn)了業(yè)務分離,同時降低了系統(tǒng)維護成本。
Redis是一個由Salvatore Sanfilippo寫的keyvalue存儲系統(tǒng)。Redis提供了一些豐富的數(shù)據(jù)類型,包括 lists、sets、ordered sets、hashes等。
Redis數(shù)據(jù)庫所有的數(shù)據(jù)都存儲在內(nèi)存中,它的讀寫操作在1 s內(nèi)超過10萬個鍵值。同時Redis也提供了RDB、AOF持久化的支持,在不影響繼續(xù)提供服務的情況下,將內(nèi)存中的數(shù)據(jù)異步寫入硬盤中,避免數(shù)據(jù)意外丟失。RDB持久化是通過將服務器某個時間點上的數(shù)據(jù)庫狀態(tài)保存到一個RDB文件中,Redis服務器可以用它來還原數(shù)據(jù)庫狀態(tài)。AOF持久化通過保存Redis服務器執(zhí)行的命令來記錄數(shù)據(jù)庫狀態(tài),被寫入AOF文件的所有命令都是以Redis命令請求協(xié)議格式保存的,Redis命令請求協(xié)議保存為純文本格式。
Redis中的事務是一組命令的集合。事務同命令一樣都是Redis的最小執(zhí)行單位,一個事務中的命令要么都執(zhí)行,要么都不執(zhí)行。一個事務從開始到結(jié)束通常會經(jīng)過事務開始、命令入隊、事務執(zhí)行3個階段。每個Redis客戶端都有自己的事務狀態(tài),保存在mstate屬性中。每一個事務狀態(tài)包含一個事務隊列以及已入隊命令的計數(shù)器,事務隊列是一個數(shù)組,數(shù)組中的每個元素保存了已入隊命令的相關(guān)信息,包含指向命令實現(xiàn)函數(shù)的指針、命令的參數(shù),以及參數(shù)的數(shù)量。
消息隊列(MQ,Message Queue)適用于異構(gòu)的系統(tǒng)之間做間接的消息傳遞,異構(gòu)系統(tǒng)兩端程序之間通過在消息包中發(fā)送數(shù)據(jù)進行通信,兩端程序?qū)嶋H上并不會直接進行通信。消息隊列作為一種中間件技術(shù),對它的使用忽略了接收和發(fā)送應用程序必須同時執(zhí)行的要求,達到了異構(gòu)系統(tǒng)間的異步通信,如果消息隊列服務器的性能足夠好,那么系統(tǒng)間的消息可以近乎實時的傳遞,達到“準同步通信”的效果。
RabbitMQ更適合于穩(wěn)定高效的企業(yè)級開發(fā),實現(xiàn)了Broker中心構(gòu)架,消息在發(fā)送給客戶端之前,必須先要在中心隊列排隊。RabbitMQ對路由、負載均衡、數(shù)據(jù)持久化都有很好的支持。RabbitMQ采用Erlang語言編寫,使用AMQP協(xié)議的高效開源消息隊列系統(tǒng),得到廣泛的應用。從框架角度來說,RabbitMQ分為客戶端和服務器端兩部分:客戶端可以是消息產(chǎn)生者(Producer)和消息消費者(Consumer);服務器端就是RabbitMQ消息隊列服務器本身[6]。
消息端從消息隊列中接收到消息后,經(jīng)過分析處理寫入集群中,然后返回給MQServer服務器ack標識,表示消費成功,繼續(xù)接收下一條消息,以此類推。
為了能夠充分體現(xiàn)常用聯(lián)系人集群新架構(gòu)可提供高并發(fā)運算處理能力,從并發(fā)性能和高可用性兩方面對集群進行測試。在測試環(huán)境中,所有測試主機均配有512 G內(nèi)存,2×10核64 bit處理器,千兆以太網(wǎng)交換模塊等環(huán)境,所有測試主機上只運行該應用,沒有其他應用共享,最大程度上保證測試環(huán)境的單一,避免外界因素對環(huán)境的影響。
1.5.1 性能測試
常用聯(lián)系人集群的性能測試主要包括寫入和讀取兩方面測試。集群有8臺服務器,每臺服務器上有4個master實例和4個slave實例,測試場景如下:
測試場景1:8臺服務器同時統(tǒng)計當前 Redis 集群數(shù)據(jù)詳情,所有服務器讀取速度可達 650萬次/s,如圖2所示。
圖2 測試場景1數(shù)據(jù)圖
測試場景2:單臺服務器統(tǒng)計當前 Redis 集群數(shù)據(jù),讀取速度可達 270萬次/s,如圖3所示。
圖3 測試場景2數(shù)據(jù)圖
測試場景3:8臺服務器同時導入數(shù)據(jù),寫入速度可達 20萬次/s,如圖4所示。
圖4 測試場景3數(shù)據(jù)圖
通過以上測試結(jié)果,常用聯(lián)系人集群在讀寫性能上較原架構(gòu)持平,Redis 服務端網(wǎng)絡帶寬最大占用 500 Mbit左右,Redis 服務端 CPU 使用率不超過45%,系統(tǒng)運行平穩(wěn)。
1.5.2 高可用測試
常用聯(lián)系人集群在系統(tǒng)性能和高可用性上,可以實現(xiàn)不停機、不停服務地動態(tài)增加或減少服務節(jié)點,系統(tǒng)能保持極高的穩(wěn)定性,當少量節(jié)點發(fā)生崩潰等故障退出服務時,集群中的其他節(jié)點可以立即接管服務,且沒有數(shù)據(jù)損失,測試結(jié)果如表1所示。
表1 測試結(jié)果
常用聯(lián)系人的增加、更改和刪除這些寫操作都是在關(guān)系型數(shù)據(jù)庫中,從關(guān)系型數(shù)據(jù)庫到Redis集群中,中間需要經(jīng)過3個環(huán)節(jié),因此兩個數(shù)據(jù)中心之間數(shù)據(jù)的一致性尤為重要。首先系統(tǒng)將同步過程中的SQL語句記錄下來,考慮到系統(tǒng)的性能和壓力,實時對每分鐘內(nèi)同步的30%比例的用戶名進行檢測,調(diào)用數(shù)據(jù)庫和Redis的接口查看數(shù)據(jù)的一致性,對于比對錯誤的數(shù)據(jù)調(diào)用同步接口進行實時同步。其次,除了準實時比對,在互聯(lián)網(wǎng)天窗時間,對每個數(shù)據(jù)庫分庫的用戶總量和Redis中的數(shù)據(jù)總量進行宏觀比對。通過準實時比對和宏觀比對,對數(shù)據(jù)的一致性提供了更全面的保障。
Redis的主從復制功能非常強大,一個master可以擁有多個slave,而一個slave又可以擁有多個slave,形成了強大的多級服務器集群架構(gòu)。主從復制不會阻塞master,也就是說當一個或多個slave與master進行初次同步數(shù)據(jù)時,master可以繼續(xù)處理client發(fā)來的請求。相反slave在初次同步數(shù)據(jù)時則會阻塞不能處理client的請求。當設置好slave服務器后,slave會建立和master的連接,然后發(fā)送SYNC命令。無論是第一次同步建立的連接還是連接斷開后的重新連接,master都會啟動一個后臺進程,將數(shù)據(jù)庫快照保存到RDB文件中,同時master主進程會開始收集新的寫命令并緩存在backlog中。master主進程發(fā)送RDB文件給slave,slave將文件保存到磁盤上加載到內(nèi)存中,master將緩存在backlog中的寫命令通過開始建立的連接發(fā)送給slave,達到主從數(shù)據(jù)的一致性。當master和slave的連接斷開時slave可以自動重新建立連接。如果master同時收到多個 slave發(fā)來的同步連接命令,只會使用啟動一個進程來寫數(shù)據(jù)庫鏡像,然后發(fā)送給所有slave[2]。Redis主從復制流程圖如圖5所示。
圖5 Redis主從復制流程圖
在運維管理控制臺中可以進行微服務、內(nèi)存數(shù)據(jù)服務和Redis 組實例的添加、刪除,以及數(shù)據(jù)遷移等操作。在集群狀態(tài)發(fā)生改變時,維護集群下所有分布式內(nèi)存數(shù)據(jù)服務狀態(tài)的一致性,圖形化界面監(jiān)控管理集群,能夠查看每秒查詢次數(shù)QPS(query per second,內(nèi)存數(shù)據(jù)大小,訪問時間RT等,如圖6所示。
實時監(jiān)控管理對集群中每個服務節(jié)點的CPU、內(nèi)存、磁盤空間大小、服務狀態(tài),以及每個集群的流量分配和相應時間RT進行檢測,報警機制分為一般、嚴重和災難3個級別。對于不同級別的報警會以不同的形式,發(fā)送給不同的人群。監(jiān)控界面如圖7所示。
圖6 監(jiān)控查詢界面圖
圖7 監(jiān)控界面
集群之間的切換是使用Zookeeper來實現(xiàn)的,用來存放數(shù)據(jù)路由表和分布式內(nèi)存數(shù)據(jù)服務節(jié)點的元信息,運維管理平臺發(fā)起的命令都會通過ZooKeeper同步到各個存活的分布式內(nèi)存數(shù)據(jù)服務節(jié)點上。提供Namespace 概念,不同集群會按照不同 product name進行組織。
在鐵路互聯(lián)網(wǎng)售票系統(tǒng)面臨的并發(fā)和存儲雙重壓力下,通過對常用聯(lián)系人集群新架構(gòu)進行性能和高可用測試,證明了采用分布式數(shù)據(jù)處理技術(shù)的新架構(gòu)可以滿足高并發(fā)需求和海量數(shù)據(jù)存儲需求,降低了單臺服務器對整個集群的影響范圍,縮短了系統(tǒng)維護時間。常用聯(lián)系人集群新架構(gòu)已上線,系統(tǒng)運行平穩(wěn),實現(xiàn)了原有架構(gòu)到新架構(gòu)的平穩(wěn)遷移。