龍 丹,徐 軻,甘 泉
(桂林信息科技學院信息工程學院,桂林 541004)
在線測評系統(tǒng)如今為人類社會提供了關(guān)于數(shù)據(jù)結(jié)構(gòu)、算法和數(shù)學的重要服務,在學術(shù)界和工業(yè)界發(fā)揮著重要的作用,特別是在競賽教育方面,使用在線測評系統(tǒng)的用戶的編程和思維能力得到了鍛煉和提升[1]。
為了解決在線提交解決方案代碼的問題,XKOJ 設計了用戶表、題目表、提交表、比賽表和博客表,構(gòu)建了完整的在線測評系統(tǒng)生態(tài)。該系統(tǒng)包含用戶注冊登錄、題目創(chuàng)建和閱讀、代碼提交驗證、比賽創(chuàng)建和運行、以及博客發(fā)表和瀏覽功能。該項目運行在云服務器上,使用Django 框架和Redis兩個Docker容器來保證安全性和可移植性。在Django 項目中,提交題目使用異步任務隊列Celery 來避免在線提交解決方案可能發(fā)生的沖突。每一個需要測評的程序都通過延時進入函數(shù),作為一個任務加入到隊列中,由隊列中的調(diào)度方案來執(zhí)行任務。通過任務文件中不同的裝飾器找到任務所需執(zhí)行的函數(shù)代碼,對提交代碼進行編譯評測,反饋結(jié)果到視圖文件中,并通過數(shù)據(jù)渲染給前端頁面,讓用戶直觀看到提交結(jié)果。
租賃云服務器,本系統(tǒng)所運行的環(huán)境為Ubuntu 20.04。在本地創(chuàng)建鏡像后,可以利用鏡像創(chuàng)建Docker容器,并在容器內(nèi)安裝項目所需的使用工具,例如Python 3、Vim 和Django-admin等。為該容器創(chuàng)建用戶并分配Sudo 權(quán)限,然后可以使用服務器端口和賬號密碼直接進行SSH登錄。
Django 默認使用關(guān)系型數(shù)據(jù)庫管理系統(tǒng)。其中SQLite 是默認的數(shù)據(jù)庫引擎,它是一個輕量級的數(shù)據(jù)庫,無需獨立的服務器進程,直接存儲在文件中。Django 的對象關(guān)系映射提供了一種將對象映射到關(guān)系數(shù)據(jù)庫中的方法,可以將對象轉(zhuǎn)換為SQL 查詢,從而在數(shù)據(jù)庫中創(chuàng)建、讀取、更新和刪除數(shù)據(jù)。
Django 的ORM 基于模型和字段的概念。模型表示數(shù)據(jù)庫中的一個表,而字段則表示該表中的一列。模型類定義了模型的屬性和行為,其中屬性是模型的字段。模型類還可以通過定義方法來實現(xiàn)模型的邏輯和行為。
XKOJ 為云服務器掛載Docker 中創(chuàng)建的項目,在Docker 中操作項目能簡化項目的部署和隔離管理,提高系統(tǒng)的可擴展性和安全性。系統(tǒng)運用了Python 語言支持的Django 框架來搭建,重點邏輯部分有三塊內(nèi)容:模型、視圖、數(shù)據(jù)庫。通過模型定義數(shù)據(jù)的結(jié)構(gòu),視圖負責處理用戶的請求并返回響應,數(shù)據(jù)庫用于存儲應用程序的數(shù)據(jù)[2]。通過鏈接調(diào)用對應的視圖中的函數(shù),來實現(xiàn)后端響應的功能,同時可以通過標識、POST 或GET 方法獲取相應的頁面內(nèi)容,并在加載的數(shù)據(jù)庫中找出對應的內(nèi)容,將處理好的數(shù)據(jù)集打包傳遞給將要加載的網(wǎng)頁中。
數(shù)據(jù)庫處理采用Django 自帶的默認關(guān)系型數(shù)據(jù)庫,通過映射的方式創(chuàng)建數(shù)據(jù)庫表,對在線測評系統(tǒng)所需的用戶、題目、比賽、博客、提交信息進行特征封裝,并在邏輯上需要時將它們相互關(guān)聯(lián),保證信息的連通性。在項目完成上,使用了部分Shell 腳本來幫助管理員解決啟用系統(tǒng)文件、代碼文件與編譯文件定量刪除等;在前端頁面上自定義了絕大部分樣式代碼,引入了Ace 代碼編輯器、Markdown、Mathjax 等開源文件增加頁面可讀性;在后端引入Celery異步任務隊列,通過創(chuàng)建任務文件編寫任務邏輯,在視圖中將打包好數(shù)據(jù)集的任務放入隊列中進行等待,在消息隊列空閑時將會通過自身的調(diào)度方式執(zhí)行一個任務。
通過Docker 將開放端口掛載Redis 緩存數(shù)據(jù)庫,Celery 使用Redis 作為消息代理,將任務放入Redis 中間件隊列,并從隊列中獲取任務以進行處理。Redis 作為消息代理,具有快速讀取和寫入的能力,可以使得Celery 的任務執(zhí)行更加高效和快速。此外,Celery 還使用Redis 作為結(jié)果存儲,保存任務執(zhí)行的結(jié)果和狀態(tài),以供查詢和分析。
測評機首先要根據(jù)提交信息的表單傳入信息,在異常處理模塊下根據(jù)聲明的數(shù)據(jù)庫變量讀取到信息數(shù)據(jù)集。程序根據(jù)使用語言執(zhí)行對應的編譯指令,在創(chuàng)建的子進程中對提交程序代碼進行編譯,通過返回的編譯文件,在子進程中產(chǎn)生輸出文件并與測試樣例輸出文件進行對比,結(jié)合編譯時的信息返回相應結(jié)果,包含通過、錯誤、編譯錯誤、超時、超限、程序異常[3]。
對于多組測試用例,則需要產(chǎn)生多組子進程來執(zhí)行編譯文件產(chǎn)生結(jié)果集,將各種情況存放在列表中,按照結(jié)果優(yōu)先級進行反饋。
對于沒有確定結(jié)果的題目,需要采取特殊判斷的方式,即按照用戶提交代碼產(chǎn)生的結(jié)果信息,根據(jù)題目要求邏輯,進行驗證程序的編寫,并將用戶提交代碼的輸出文件輸入到驗證程序中運行,若滿足所有條件,或輸出期望結(jié)果,則返回此提交結(jié)果正確,否則依舊根據(jù)結(jié)合編譯時的信息,按照各種情況的優(yōu)先級進行反饋[4]。
圖1 測評機處理任務流程
在XKOJ中,訓練模式對應標題單,比賽模式對應比賽。在查看題目之后的所有操作都相同,但是會調(diào)用不同的測評機,傳遞不同的比賽信息。在XKOJ中,視圖會根據(jù)不同的比賽ID將提交結(jié)果保存在不同比賽外鍵的表中,在后續(xù)查看比賽排名與提交信息時,能夠清晰地顯示當前比賽中的各種信息。但如果是在題單中提交代碼,則不會將其添加到任何比賽中,而是會保存在無比賽外鍵的表中,為用戶提供狀態(tài)查看與代碼查看。
圖2 練習、比賽共用題庫模塊需求
系統(tǒng)功能總體分為四個類別:設置、題目、博客、比賽。
在每個類別下,需要編寫對應的視圖函數(shù)實現(xiàn)需求。
設置:登錄、登出、注冊、查看用戶信息。
題目:查看題目列表、查看題目內(nèi)容、提交解決方案、查看提交結(jié)果。
博客:查看文章列表、編寫博客、查看文章內(nèi)容、點贊、評論。
比賽:創(chuàng)建比賽、報名、查看比賽列表、查看比賽排名、查看提交狀態(tài)。
實現(xiàn)不同的視圖函數(shù),需要建立對應的數(shù)據(jù)表,用來支持系統(tǒng)完成相應的操作。
圖3 XKOJ主要數(shù)據(jù)表結(jié)構(gòu)系統(tǒng)重點視圖函數(shù)與數(shù)據(jù)庫表
用戶是在線測評系統(tǒng)一個重要的組成部分,系統(tǒng)需要具體實現(xiàn):注冊、登錄、填寫、顯示和修改個人信息功能,并保證用戶提交題目或者比賽會產(chǎn)生相應的練習數(shù)據(jù)與比賽數(shù)據(jù),并能進行查詢。
一個在線測評系統(tǒng)需要驗證用戶根據(jù)題目要求編寫的代碼是否正確,即需要設計一個滿足評測需求的測評機。測評機需要識別語言和代碼信息,并編譯運行程序,判斷程序是否符合要求,將結(jié)果數(shù)據(jù)打包返回。
對于處理多用戶多提交解決方案代碼的實際情況,首先應保證測評機能穩(wěn)定有序地返回評測結(jié)果,其次應盡可能優(yōu)化測評機的評測水平以提高其性能??紤]到安全性,防止提交代碼內(nèi)包含惡意攻擊代碼,應盡可能使用子進程去驗證并執(zhí)行子任務。
圖4 測評機功能分析
題庫是在線測評系統(tǒng)的核心,有了階梯難度分布的廣泛題庫,才能更好地鍛煉用戶的代碼水平[5]。
解決問題的代碼編寫需要在限定的時間和空間復雜度內(nèi)考察用戶的算法能力,并對數(shù)據(jù)范圍有敏感的認知。通過各表間的相互聯(lián)系,共同實現(xiàn)在線測評系統(tǒng)的一整套流程。
博客是在線測評系統(tǒng)的重要組成部分,用戶可以通過編寫博客的方式進行輸出,分享解決問題或積累的知識,還可以通過博客內(nèi)的各類知識與題解提高自己的學識。博客查閱需要舒適的閱讀體驗,可以通過封裝Markdown 提高內(nèi)容的可讀性。
比賽是一個封裝的功能,需要保證每場比賽之間的獨立性。同時,在邏輯上,XKOJ 將題單作為沒有開始與結(jié)束時間的比賽放在比賽頁面中。用戶點擊比賽時,將跳轉(zhuǎn)到報名信息界面進行報名。如果用戶未報名,則返回報名成功;如果已報名,則進入具體比賽界面。通過不同表單之間的聯(lián)系,匹配用戶、題目、比賽三元組的信息,在比賽期間實時更新排名。在提交表中加入用戶提交本題的錯誤次數(shù)、正確標識和正確時間;在排名表中加入用戶的總通過數(shù)和總花費時間,作為特征依據(jù)“通過數(shù)量多者優(yōu)先,通過數(shù)量相同者花費時間小者優(yōu)先”的規(guī)則,對每位參加比賽的用戶進行排序。
對于提交狀態(tài),只需要在比賽內(nèi)的測評機內(nèi)不斷更新任務隊列中已處理的任務信息即可。同時,根據(jù)自定義需求,在網(wǎng)頁上顯示部分特征。
關(guān)于比賽題目查閱問題,本系統(tǒng)采用通過超鏈接傳遞比賽ID 的方式,讓題目在提交時首先判斷是通過練習題單提交還是通過比賽提交,并調(diào)用不同的測評機進行評測。這樣能夠很好地將提交信息封裝到每場不同的比賽中,保證數(shù)據(jù)清晰。
Celery是一種基于Python 的異步任務隊列消息代理,它提供了分布式任務調(diào)度和處理的功能。它可以與多種后端存儲集成,支持任務的異步執(zhí)行,可靠性高、可擴展性強。
測評環(huán)境為阿里云服務器2 核2 GB,測試內(nèi)容為使用Celery 作為任務隊列來實現(xiàn)一個測評機任務隊列,Redis 作為支持AMQP 的消息代理傳遞消息。通常情況下,Celery支持處理數(shù)以萬計的任務,但是任務隊列的吞吐量會受到消息代理的限制、任務處理時間的約束。
本次測試通過程序編寫,模擬用戶提交解決方案,并發(fā)提交5000 個任務到任務隊列中,并記錄完成這些任務所需要的時間。為了測試測評機的吞吐量,測試了使用1 個測評機和4 個測評機的情況完成所有任務所需的時間,并計算出每秒可以測評的任務數(shù)量。
進行兩組測試,一組測試需要計算兩個數(shù)的和;另一組測試題目為圖論經(jīng)典題目最大流,需要在有向圖中找到一條從原點到匯點的路徑,使得路徑上的最小邊權(quán)最大。
表1 測評機吞吐量測試
經(jīng)過表格內(nèi)的測試結(jié)果,在任務量很大時測評機依舊可以保持不錯的狀態(tài),使用4個測評機時效率大概為單測評機的四倍。這說明,在處理較大的任務量時,使用Celery 可以顯著提高任務處理的效率,減少任務完成的時間,提高系統(tǒng)的性能。因此,在本系統(tǒng)的使用需求下,采用Celery實現(xiàn)任務隊列是不錯的選擇。
近年來,在線評測系統(tǒng)已經(jīng)被廣泛應用于各個領(lǐng)域。隨著計算機科學技術(shù)的不斷發(fā)展,在線評測系統(tǒng)也在不斷進步和完善。未來,這些系統(tǒng)將引入更多的智能技術(shù),如機器學習和深度學習,以提高評測的準確性和效率[6]。同時,為了保證比賽評判的公正性,將會為比賽參賽者提供更高效的代碼查重機制,并引入更加公正和客觀的評判機制。這些系統(tǒng)將根據(jù)不同用戶的需求和特點,提供更加個性化的學習內(nèi)容和評測體驗。未來,在線評測系統(tǒng)將會不斷更新題庫,覆蓋更多的算法和數(shù)據(jù)結(jié)構(gòu)等知識,同時添加更多實際應用場景下的編程題目,以幫助程序員更好地解決現(xiàn)實生活中遇到的問題。
此外,未來的在線評測系統(tǒng)將建立更加開放的社區(qū)生態(tài),注重社區(qū)的建設和發(fā)展,為用戶提供更豐富的交流和互動機會,促進用戶之間的交流和學習。同時,這些系統(tǒng)將提供更加高效的測試和調(diào)試工具,幫助程序員更快地發(fā)現(xiàn)和解決程序中的問題。它們還將為新出現(xiàn)的編程語言提供機會和平臺,以便它們傳播、交流和發(fā)展。不僅如此,這些系統(tǒng)還將為不同需求的群體提供一個計算機科學技術(shù)交流的平臺,促進計算機科學技術(shù)的發(fā)展。