李 騫,胡揚帆,劉 培,馬艷彬
(新華通訊社 通信技術局,北京 100031)
近年來,隨著互聯網技術的飛速發展,各企事業單位為了更好的履行職能,提高辦公效率,提升管理水平,都在大力發展信息化建設.投票評審系統作為促進業務發展的一種手段在各單位已成為常態化,投票表決事項、投票規則、統計方式等也趨于多樣化.傳統的投票評審方式準備周期長且復雜,系統擴展性較差,難以應對投票過程中產生的各類突發情況,用戶并發操作時,系統性能下降明顯.如何利用先進的科學技術和手段替代傳統低效率的投票評審方式成為了亟待解決的問題.
本系統基于有限狀態機原理,利用WebSocket等互聯網技術設計并實現了一個實時高可靠的投票評審系統.本系統具有較高的擴展性,可通過靈活配置,滿足基于人員、事項等多種對象的投票信息化管理,涵蓋了多種投票形式.
圖1所示的功能架構中,管理員通過管理端進行會議及投票計劃的創建,并對投票流程進行控制.評委通過評委端進行投票及投票結果的實時查看.

圖1 系統功能架構圖
如圖2所示,系統使用了Nginx+Node.js+MongoDB的開發框架進行快速迭代開發.通過Machina.js組件實現系統中有關狀態機的各種邏輯.通過Socket.IO組件運用WebSocket技術實現系統應用前后端實時通信,并保證了應用系統的高可靠性.

圖2 系統技術架構圖
如圖3所示,投票模型(Voting Model,VM)是投票流程的狀態機模型,主要用于處理計算及流程狀態相關邏輯.

圖3 VPM和VP的關系
投票處理器(Voting Processor,VP)主要負責投票流程中的數據預/后處理、存取邏輯并負責具體處理WebSocket請求,一個VP對象實例管理并操作對應的一個VM對象實例,一對VP與VM形成一套投票流程模板,具體負責一類投票流程.
投票處理器管理器(Voting Processor Manager,VPM)主要負責多個VP實例的管理,包括VP的加載、同步、切換等等,并負責處理轉發WebSocket請求至相應的VP實例.
有限狀態機(Finite State Machine,FSM)是計算機領域中一種用來表示有限個狀態以及在這些狀態之間的轉移和動作等行為的數學模型[1].
有限狀態機可以用來進行對象行為建模,其作用主要是描述對象在它的生命周期內所經歷的狀態序列,以及如何響應來自外界的各種事件[2].
在本系統中,使用了JavaScript的有限狀態機庫Machina.js來實現狀態機相關的邏輯.
由于HTTP協議的請求-響應特性,Web應用都是以一種單向的方式進行通訊——所有的通信都是由客戶端發起和控制的.于是許多網站為了實現向客戶端實時推送數據,采用了一種輪詢的技術,即每隔一段時間由客戶端向服務端請求最新數據[3].然而由于服務端需要不斷的響應客戶端發出HTTP請求,造成了很大的網絡資源浪費.圖4是采用輪詢技術和WebSocket的網絡負載對比[4].

圖4 輪詢和WebSocket實現方式的網絡負載對比圖
由此可見,采用輪詢技術的網絡負載遠遠大于WebSocket技術的網絡負載.
于是,HTML5標準定義了WebSocket協議.該協議在客戶端和服務端之間建立了一個全雙工的套接字連接,客戶端和服務端會通過這個持續存在的連接通道自由地傳遞數據.WebSocket協議是基于TCP的一種通信協議,所以其工作流程也需要經過發起連接請求、握手、建立連接三個步驟[5]:
1)客戶端發送連接請求;
2)服務器響應并切換協議;
3)通信兩端建立全雙工連接.
最后當通信中的某一方認為可以結束會話時,便會給對對方發送關閉連接的請求,這樣便結束了此次連接.
WebSocket協議工作流程如圖5所示.

圖5 WebSocket協議工作流程
作為一種新的Web標準,雖然WebSocket協議仍在發展中,卻已經具有一系列優秀的特性.首先,WebSocket是一種有狀態的協議,所以通信時可以省略部分狀態信息,提高了數據傳輸的效率,具有更強的實時性;其次,WebSocket有更好的二進制支持,相對于HTTP可以更好地處理二進制內容;最后,WebSocket支持擴展,用戶可以由此實現用戶自定義的子協議[6].
本系統中我們采用了開源的跨平臺實時通信庫Socket.IO.它提供了客戶端和基于Node.js服務器的解決方案.
Node.js是一個服務器端的JavaScript運行環境,由于封裝了Google的V8引擎,使得其執行JavaScript代碼的速度和性能都有很大提升.Node.js的事件驅動和非阻塞I/O特性,使得它通常被用于I/O密集的實時應用系統[7].
傳統的Web服務器技術中,每當新增一個連接請求時便會生成一個新的線程進行處理和響應[8].而Node.js使用了非阻塞的單線程方式,利用JavaScript的事件機制,在收到一個查詢請求后便將CPU的控制權交出,直到當數據處理完成后觸發一個事件,再取得控制權繼續執行.
如圖6所示,Node.js的結構與Chrome非常相似,他們都是基于事件驅動的異步架構.瀏覽器通過事件來處理界面上的交互,Node.js通過事件來處理I/O.在服務器端,Node.js雖然不再處理CSS、與DOM和BOM打交道,但是卻具有JavaScript的一切語言特性——基于原型鏈的對象繼承、事件處理機制、回調函數等.此外,Node.js還可以方便地訪問本地文件、數據庫以及網絡等資源[9].

圖6 顯卡Chrome瀏覽器和Node.js的組件構成
在本系統中,我們采用了Express作為Node.js Web框架.Express是一種簡潔易用、功能強大的Web應用程序框架,它提供了一系列豐富的API及中間件用于進行快速開發.我們還使用了基于分布式文檔存儲的非關系型的MongoDB作為底層數據庫.
下面我們將首先聚焦于有限狀態機在VM(投票模型)實現中所起到的作用,然后我們還將具體描述投票流程以及VM、VP、VPM的聯動關系,并闡述系統可靠性的實現方法.
本系統支持多種投票形式,如差額投票、多輪次投票、候選逐一投票等等,并支持將來的擴展.我們整理了十幾種不同的投票場景、規則,將它們進行歸納整理,并最終使用有限狀態機(FSM)來對投票流程進行建模.
我們舉一個比較簡單的投票場景為例,其規則如下:
1)X個候選人中選出Y(目標額度)個人通過(X>Y);
2)根據候選人所得贊成票多少進行統計,須得到與會評委相應比例(如2/3)的贊成票方有資格通過;
3)滿足規則2的候選人超過規定額度時,按得票多少取滿目標額度;
4)在規則3情景下,如出現目標額度位置前后得票并列的情況,則對并列的候選人重新投票,直到選滿目標額度為止.假設我們按票數從高到低對候選人進行排列,如出現票數Y=票數Y+1=票數Y+2或票數Y-1=票數Y=票數Y+1的情況就需要進行并列邏輯處理,如出現票數Y-1=票數Y且票數Y>票數Y+1的情況,則不需要進行并列邏輯處理.
針對此投票場景,我們使用如圖7所示的狀態機來對其投票流程進行建模.
該狀態機共有5個狀態,各狀態及轉移情況描述如下:
(1)初始狀態(uninitialized)
接收init事件及相關參數后,轉移至knockout狀態,并執行init初始化方法對首輪投票參數進行初始化.
(2)投票狀態(knockout)
接收init事件及參數來初始化投票輪次.
每一張投票觸發一次vote事件,記錄選票,直到所有評委均投票完成,觸發計票邏輯,計算本輪投票結果.若未滿足投票目標則狀態機轉移至rest狀態,計算好下一輪投票可能的參數(如剩余額度、評委投票權數(評委可投的票數)、候選項等),準備進行下一輪投票;若滿足投票目標則狀態機轉移至goal狀態,完成投票計劃.
(3)投票輪次結束狀態(rest)
接收init事件及相關參數后,轉移至knockout狀態,并執行init初始化方法對下一輪投票參數進行初始化.
接收abort事件,轉移至aborted狀態,終止本次投票計劃.
(4)最終態(goal)
投票正常完成.
(5)最終態(aborted)
投票被手動終止.
根據此狀態機設計,投票規則1)至4)中所涉及的計算邏輯可在knockout狀態的vote事件的計票邏輯中實現,從而可完成單輪或多輪的投票場景,并可人工進行終止.
針對更復雜的投票場景,如含有多個規則不同的環節的投票計劃(如含有預選、復議、定評等環節),我們可以建立多個針對不同環節的狀態,如qualification、knockout、finalreview,完成各自的初始化和記/計票邏輯,并復用uninitialized、rest、goal、aborted狀態的相關邏輯,便可完成該投票場景的計算模型.
狀態機的實現上我們使用了machina.js框架,一個成熟的javascript狀態機框架.在該框架中我們完成了狀態、狀態轉移、狀態事件的定義,由于machina.js定義的FSM是基于EventEmitter的,所以我們方便地進行了輸出事件的定義.關鍵代碼如下:


系統采取了以數據庫為中心的計算與IO分離的設計策略,對于每一步經過確認的數據先存入數據庫,存儲成功后再進行下一步處理,每一次的投票狀態變化都先存儲或更新數據庫再進行下一步數據操作.以步步為營的方式記錄最新的投票狀態,這樣保障了數據的一致性,同時為狀態恢復機制提供了依據.
如圖8,整個投票過程的前后端數據交互都由WebSocket協議實時傳遞,評委端的所有選票數據由WebSocket發送給后端的VPM,VPM對每一份收到的數據進行驗證、收集、存儲和計算,得出投票結果后通過WebSocket對評委端、管理端和投票監控大屏等展示終端進行實時廣播,各端收到投票結果數據立即進行展示.

圖8 系統數據流圖(數字標號表示順序)
具體為:首先選票數據由評委端發送給VPM,VPM交給對應投票類型的VP,VP對每一份選票數據進行驗證后連同評委信息一起存入數據庫,僅當存庫成功后才進行步驟3,將選票傳入VM狀態機用于收集和計算,當所有與會評委投票完成后,VM自動計算投票結果,并將結果返回給相應VP,VP進行存庫,最后廣播結果數據給前端.
投票過程中數據的驗證、傳遞和存儲由VP負責,它是基于持久化存儲的,非易失的,可靠性強;計算由VM負責,VM的數據運行在內存中,是易失的,可靠性弱.當系統經歷意外宕機等情況時,VP可以從數據庫中獲取宕機前投票計劃的最后狀態的數據,然后用計劃狀態數據初始化VM狀態機和前端頁面,即可恢復投票狀態.
投票過程中服務端和客戶端都可能出現意外情況而斷電或斷網,因此服務端和客戶端都需要完備的狀態恢復機制.
對于服務端,當系統經歷意外中斷之后,重啟程序時VPM會先從數據庫中讀取尚未結束且處于活動狀態的計劃,然后VP依據具體的計劃狀態和票箱(已投)數據確定恢復計劃的具體數據,包括當前的輪次信息,評委提交信息,組裝好具體數據后VP將其廣播給各前端(評委端、管理端、投票監控大屏),主動刷新了前端的狀態.服務端的相關程序,如應用、數據庫、nginx等程序都部署了開機自啟動腳本,因此整個投票系統可以在無人工干預的狀態下自動恢復到投票中斷前的狀態.
由于前端設備數量較多,可控性較差,在投票的任何階段都可能出現終端連接斷開和重新連接的情況,因此投票過程中一旦有前端發起同步請求,服務端先檢視當前投票狀態,根據當前狀態準備前端的恢復數據,返回給前端進行頁面更新.

圖9 系統時序圖
圖9所示的時序圖主要描述了前端和服務端之間的數據交互.圖中所有消息都是異步發送,由socket.emit事件觸發,然后監聽返回事件,依據返回數據進行頁面更新.以ctrl開頭的指令表示控制指令,控制投票狀態,例如投票開始;以view開頭的指令表示更新頁面.根據時序圖步驟0,每個新建立的WebSocket連接都會收到服務端發出的sync同步指令,該指令數據中定義了投票狀態和渲染頁面所需的信息,前端根據sync指令的數據來更新頁面內容.此步驟保證了前端頁面隨時都可以與服務端通過WebSocket接口恢復投票狀態.
當投票正在進行時,前端步驟3發送的投票數據在服務端收到后會廣播viewdata:vote投票進程數據,各端頁面收到后會即時更新當輪投票的進度.如果當輪投票結束且滿足整個投票結束的條件后,服務端根據步驟3.3也會將結果數據廣播給各前端頁面,當管理員手動終止投票時也是同樣的流程.
這樣,系統以數據庫為中心,采用WebSocket實時雙工數據傳輸協議,以投票狀態為依據,從應用設計和部署層面實現了投票的高可靠性.
本系統目前已經正式投入使用,完成了本單位多次社領導級別的重要評審會議的技術保障工作,包括社編務會通報表揚事項表決、采編業務考核非固定加減分事項表決、全社創新工作獎勵集中評審表決、年度社級優秀新聞作品評審表決、駐外分社職稱評審表決等等.
投票模板的應用使得切換投票方式變得簡單易行,從前端展示到后臺統計均可一次性完成,大大節省了投票準備工作時間.傳統投票系統在輪次間需要人工進行下輪參數的計算和設置,平均人工準備時間大約需要2到5分鐘.使用本系統后,評審過程中系統自動計算出各種參數(如候選項),流程銜接順暢,沒有了人工準備時間,極大地提高了評審效率.系統智能程度較高,學習成本低,簡單易懂的操作界面提升了用戶體驗.高可靠性使得系統能靈活應對各種突發事件,確保數據正確完整.
“界面新穎、體驗良好、功能靈活、流程順暢”的新一代投票評審系統,投入使用后效果顯著,得到了本單位社領導、各部門領導和組織單位的一致好評.
本系統具有較高的可靠性,滿足基于人員、事項等多種對象的投票信息化管理,并可擴展多種模板以滿足不斷增長的業務需求.系統的使用極大地提高了評審管理效率,增加了評審工作的標準化與科學化,互聯網思維的用戶體驗也增加了用戶的黏合度.未來,計劃在現有系統基礎上增加基于系統使用數據的大數據分析,輔助領導決策,促進業務發展.
1錢忠勝,鄒俊.正規文法與有限自動機的等價構造.計算機應用與軟件,2008,25(6):110-112.
2為Linux應用構造有限狀態機.https://www.ibm.com/deve loperworks/cn/linux/l-fsmachine/.[2014-10-01].
3易仁偉.基于WebSocket的實時Web應用的研究[碩士學位論文].武漢:武漢理工大學,2013.
4WebSockets簡介:將套接字引入網絡.https://www.html5rocks.com/zh/tutorials/websockets/basics/.[2010-10-20].
5李錫輝,楊麗.基于WebSocket的服務器推送技術研究.網絡安全技術與應用,2014,(6):45-46.
6齊華,李佳,劉軍.基于Websocket的消息實時推送設計與實現.微處理機,2016,37(3):36-39,43.
7萬里晴,楊浩.探究基于V8引擎的Node.js在各應用領域的發展.通訊世界,2015,(13):97.[doi:10.3969/j.issn.1006-4222.2015.13.066]
8駱文亮.Node.js服務器技術初探.無線互聯科技,2014,(3):227.
9樸靈.深入淺出Node.js.北京:人民郵電出版社,2013.