戴施偉,周凌珉,鄭一泓
(浙江水利水電學院信息工程學院軟件工程系,浙江 溫州 325204)
OJ(Online Judge)系統,是近十幾年隨著ACM 事業在中國的發展而引入的一種在線練習和比賽系統,最早設計服務于ACM-ICPC 國際大學生程序競賽的自動判題和排名,使用者可在線提交多種程序的源代碼(如C、C++、Java),系統會對源代碼進行編譯并運行,再通過后臺數據庫的測試用例來檢驗代碼的正確與否。隨著ACM 的發展和我國的信息化教學方式的變革,國內一些高校也開發了屬于自己的OJ 系統,其中也不乏有許多主流的OJ,杭州電子科技大學的HDOJ,浙江大學的ZOJ……。通過建立本校的OJ,不僅可以方便平時ACM 隊員的訓練和競賽模擬,還可以添加獨創的題目,擁有屬于本校自己的題庫;在日常教學方面,OJ系統不僅可以提高計算機專業的學生學習數據結構算法的積極性和學習效率,也為所有想提高自己程序算法能力的學生提供了一個練習的平臺。
結合實際需求,在框架整體的設計中本系統著重考慮了在高并發訪問下的效率問題。
系統使用了Spring Boot 作為開發框架,它對于Spring中存在的重量級配置問題、項目的依賴管理、不支持分布式等問題進行了改善和優化,精簡配置的同時方便對外輸出各種形式的服務。考慮到安全性以及系統性能,采用判題服務與后臺服務分離的架構優化方案,減少服務器負擔的同時降低開發人員對系統維護的難度。兩者間通信采用RabbitMQ 消息隊列,既可以提高并發量,又能降低服務之間的耦合度,且具有更好的時效性。采用MySQL 進行數據持久化存儲,其服務靈活,快速穩定。最后前端頁面的開發使用Vue.js框架,簡潔高效。
本判題系統劃分為三部分:前端交互、判題系統、后臺系統(見圖1)。在前端頁面中,根據功能需要,分別對每個角色進行界面設計。下面主要介紹判題模塊與后臺服務模塊的功能。

圖1 系統思維導圖
1.2.1 判題模塊設計
為了系統的安全性,判題模塊應與后臺服務模塊相分離,使用RabbitMQ 消息隊列實現通信,采用心跳機制,定時向后臺服務端發送判題端系統信息,用于展示判題端在線情況。為了便于查看管理,判題系統應地向后臺系統上報判題系統運作狀態(見圖2)。當收到用戶提交信息時,判題系統應進行數據交互,使后臺服務端處理判題請求(見圖3)。

圖2 判題系統活動圖

圖3 后臺處理判題請求活動圖
1.2.2 后臺服務模塊設計
后臺系統是實現前端頁面操作和系統功能的重要模塊,本系統將后臺系統分為七個模塊,以下主要介紹四個功能模塊的設計。
⑴競賽模塊:當收到判題結果信息后,系統會對競賽信息進行相關更新(見圖4)。管理員和教師能在后臺添加新競賽,并且可以設置競賽顯示時間、開始時間、結束時間、競賽總時長等。管理員和教師也能對自己權限范圍內的競賽進行修改,進行用戶管理。設置測試賬號后可以進行競賽測試。設立排行榜,對競賽成績按照編程競賽規則排名。可以對前臺采集的用戶數據進行儲存、分析、IP 上報,防止用戶作弊。提供競賽加入功能,用戶在競賽過程中可以直接加入開放型競賽,對于私人競賽可以通過房間ID 和密鑰加入。對于競賽數據如用戶答題情況、用戶提交情況、用戶競賽IP 數據、排行榜,可以進行查閱和導出,同時競賽的提交數據也可以進行重新判題。

圖4 后臺系統處理判題結果活動圖
⑵練習模塊:用戶可以查閱系統中公共類型的問題內容,并按答題情況差異化展示。設立排行榜,對用戶按已解決問題數進行排名。
⑶問題模塊:管理員或教師能添加新的問題,也對自己權限范圍的問題信息進行修改、刪除,還可以添加或刪除測試數據。
⑷討論模塊:管理員可以在系統設置中開啟討論區,用戶能在討論區中發起討論,可以和其他用戶進行評論互動。
本判題系統數據儲存使用MySQL。判題系統模塊與后臺服務模塊分離,模塊間通信擯棄現有OJ系統普遍采用的直接讀取數據庫的方案,使用RabbitMQ消息隊列實現通信以提高安全性。規范數據庫編碼設計,防止攻擊者通過sql 注入對后臺數據庫的數據進行讀取、刪除、篡改。
本系統采用Spring Boot 自帶的定時器定時調用MySQL 自帶的mysqldump 命令來實現數據庫導出到文件,然后通過SMTP郵件發送服務,將數據庫備份文件打包發送給系統預置的管理員郵箱,實現數據庫備份操作。設計時引入Docker,將判題服務封裝成容器,一方面實現簡易部署,另一方面Docker 容器相較于直接在宿主機運行擁有更好的安全性。
前端頁面基于Vuexy Admin Template模板開發,界面基礎輔助色為灰黑紫,簡潔大方。頁面設計中,為用戶每一類型的操作提供明確的反饋,同時也設計了對話框以及錯誤提示。輸入框采用簡單的描述文字,并使用左對齊,以符合閱讀視線規律,選擇恰當的文字顏色給予用戶輸入反饋,對于錯誤輸入給予紅色提示,對于正確輸入無提示或者給予綠色提示,對于危險輸入給予黃色提示。致力于設計一款用戶友好型操作界面。
用戶界面設計為左右布局,左邊用于欄目導航,右邊用于欄目內容的展示,便捷高效的同時讓頁面看起來更加活躍(見圖5)。

圖5 頁面布局概覽
引入插件apexcharts 進行對部分場景數據的圖形繪制,用更加直觀的數據圖展示內容(見圖6)。

圖6 個人答題情況扇形圖
對于表格類數據進行數據分級,不同類數據采用不同配色,直觀化展示各類信息,使用戶在了解信息優先級的同時對信息有更快速更準確的定位(見圖7)。

圖7 個人歷史答題情況圖
對于用戶權限驗證的實現,本系統通過客戶端對Header(請求頭)中的Authorization 字段里攜帶的token令牌進行用戶的權限校驗,客戶端發出的所有請求都會攜帶這個令牌,當用戶注銷,令牌就會在客戶端進行銷毀,由于token 無狀態的特點,服務器不需要保存令牌的狀態,可以有效降低服務器進行數據庫查詢的次數。在用戶登錄的時候,系統會根據用戶的個人信息和登錄時間生成一個具有有效時間限制的token(令牌)。
本系統使用filter(過濾器)和interceptor(攔截器)對部分需要權限的頁面進行授權校驗,從Header 中獲取令牌,對其進行Base64Decode解碼后對authorization字段進行驗證。驗證通過后,修改Header注入用戶ID和用戶等級信息。當驗證的令牌的信息無效或者錯誤時,拒絕訪問當前請求的頁面并返回授權錯誤信息。
本系統采用RabbitMQ 消息隊列實現后臺服務與判題服務器之間的通信主要服務內容有:向判題服務器發送判題請求,向后臺服務發送判題服務器返回的判題結果,實現心跳機制——定時向后臺服務端發送判題端的系統信息。MQ 服務器中的重發機制所帶來的因數據量過多導致內存溢出的危險,可以通過調用ChannelAwareMessageListener 接口實現來ack 機制來消除。
判題服務器在接收RabbitMQ 消息隊列中的判題請求之后,將提交的代碼寫入臨時文件。根據請求者選擇使用的語言類型,選擇不同的編譯器進行編譯并生成可執行文件。文件生成完畢即使用題目所對應的測試用例進行測試,執行完的結果保存至.out 后綴的文件中,通過用Ptrace 對進程進行跟蹤,執行完畢刪除臨時文件,并使用題目所對應的樣本標準測試結果文件和執行生成的文件進行比對,判斷的結果通過RabbitMQ消息隊列返回。
伴隨信息化教育的普及,OJ系統的應用場景也順應增加,本系統在保證原有功能的基礎上,增加了對應教學場景的功能,對成績和代碼的導出,反作弊管理的加強。本系統的后期測試結果表明所有功能運行正常,也符合了預期性能要求。本系統正式運行后在日常教學環境中得到了良好應用,不僅能夠滿足ACM隊員的日常訓練、模擬比賽、開展競賽,也能作為教師開展相應程序類設計課程的教學輔助工具,為學校所有計算機類學生和想提高自己算法能力的學生提供了一個便捷有效的平臺。