【从零构建分布式在线评测系统】二、Judge0服务器上的心跳程序
上一篇介绍了整体构架这一篇介绍如何实现心跳程序。在ubuntu上使用.net程序有不少方法我们采用使用标准框架的方式。一、工程配置1、类型控制台应用——莫担心它会得到一个dll部署这个dll即可2、目标框架.net 8.03、目标OS无二、心跳客户端在分布式评测系统中Judge0服务器节点需要向管理端位于OJ服务器上Manager报告自己的状态。并且需要1、尽可能占用较少资源不影响Judge0评测性能2、Manager故障不影响心跳发送3、支持多个Manager地址和参数调账4、跨平台2.1配置文件管理Imports System.IO Imports System.Xml.Serialization Serializable Public Structure Judge0ServerConfig Public OJURL() As String Manager服务器地址列表 Public Judge0Port As Integer Judge0服务端口 Public Group() As Integer 服务器组别 (0低性能, 1高性能) Public QueueMaxSize As Integer 队列最大容量 Public RequestCountSize As Integer 最大并行请求数 Public AvailableMemoryThresholdOfMB As Double 内存保护阈值(MB) Sub New(OJURL() As String, Judge0Port As Integer, Group() As Integer, QueueMaxSize As Integer, RequestCountSize As Integer, AvailableMemoryThresholdOfMB As Double) Me.OJURL OJURL Me.Judge0Port Judge0Port Me.Group Group Me.QueueMaxSize QueueMaxSize Me.RequestCountSize RequestCountSize Me.AvailableMemoryThresholdOfMB AvailableMemoryThresholdOfMB End Sub End Structure Public Class Config Private Shared filePath As String AIOJ_Judge0.conf summary 序列化配置文件首次运行生成示例配置 /summary Friend Shared Sub SerializeJudge0Config() 示例包含内网和外网两个Manager地址 Dim config As New Judge0ServerConfig( {http://192.31.116.222:6666, http://xxx.xxx.xxx.xxx:6666}, 2358, {0, 1}, 100, 4, 200 ) Dim serializer As New XmlSerializer(GetType(Judge0ServerConfig), {GetType(String())}) Using writer As New StreamWriter(filePath) serializer.Serialize(writer, config) End Using Console.WriteLine($[{DateTime.Now}] 配置文件已生成: {filePath}) End Sub summary 反序列化配置文件 /summary Friend Shared Function DeserializeJudge0Config() As Judge0ServerConfig Try Dim serializer As New XmlSerializer(GetType(Judge0ServerConfig), {GetType(String())}) Using reader As New StreamReader(filePath) Return CType(serializer.Deserialize(reader), Judge0ServerConfig) End Using Catch ex As Exception Console.WriteLine($[{DateTime.Now}] 读取配置文件失败: {ex.Message}) Console.WriteLine(请确保配置文件存在或运行 SerializeJudge0Config 生成示例配置) Return New Judge0ServerConfig(Nothing, -1, {-1, -1}, -1, -1, -1) End Try End Function End Class2.2心跳消息发送Imports System.Net.Http Imports System.Text Imports System.Text.Json Imports System.Threading Serializable Public Class HeartbeatMessage Public Property Port As Integer Judge0服务器端口 Public Property QueueMaxSize As Integer 队列最大大小 Public Property RequestCountSize As Integer 同时最大投递数 Public Property Group As Integer 所属组别 Public Property AvailableMemoryOfMB As Double 可用内存大小(MB) Public Property AvailableMemoryThresholdOfMB As Double 内存保护阈值 End Class Public Class Heartbeat Private Shared ReadOnly _httpClient As New HttpClient() summary 发送心跳请求 /summary param namePortJudge0端口/param param namegroup组别/param param namesubmissionURLManager接收地址/param param namequeueMaxSize队列最大容量/param param namerequestCountSize最大并行数/param param nameavailableMemoryMB可用内存/param param nameavailableMemoryThresholdOfMB内存阈值/param param nametimeoutSeconds超时时间(秒)/param returns0成功, 负数错误码/returns Public Shared Async Function SendRequest( Port As Integer, group As Integer, submissionURL As String, queueMaxSize As Integer, requestCountSize As Integer, availableMemoryMB As Double, availableMemoryThresholdOfMB As Double, timeoutSeconds As Integer ) As Task(Of Integer) 构建心跳消息 Dim jsonContent As New HeartbeatMessage With { .Port Port, .Group group, .QueueMaxSize queueMaxSize, .RequestCountSize requestCountSize, .AvailableMemoryOfMB availableMemoryMB, .AvailableMemoryThresholdOfMB availableMemoryThresholdOfMB } 序列化为JSON Dim content As New StringContent( JsonSerializer.Serialize(jsonContent), Encoding.UTF8, application/json ) Using cts As New CancellationTokenSource(TimeSpan.FromSeconds(timeoutSeconds)) Try 发送POST请求 Dim response As HttpResponseMessage Await _httpClient.PostAsync( submissionURL, content, cts.Token ) If response.IsSuccessStatusCode Then Return 0 成功 Else Console.WriteLine($[{DateTime.Now}] {submissionURL} 请求失败状态码: {response.StatusCode}) Return -2001 End If Catch ex As TaskCanceledException When cts.Token.IsCancellationRequested 超时处理 Console.WriteLine($[{DateTime.Now}] {submissionURL} 请求超时{timeoutSeconds}秒) Return -3001 Catch ex As Exception Console.WriteLine($[{DateTime.Now}] {submissionURL} 发送错误: {ex.Message}) Return -1001 End Try End Using End Function End Class2.3系统监控模块Imports System.Diagnostics Imports System.IO Public Class ServerMonitor Private Shared ReadOnly _memInfoPath As String /proc/meminfo summary 获取Ubuntu系统可用内存单位MB 直接从 /proc/meminfo 读取性能优于调用系统命令 /summary returns可用内存(MB)失败返回-1/returns Public Shared Function GetUbuntuAvailableMemoryMB() As Double Try 检查文件是否存在 If Not File.Exists(_memInfoPath) Then Console.WriteLine($错误: {_memInfoPath} 文件不存在) Return -1 End If 读取内存信息 Dim memInfoLines As String() File.ReadAllLines(_memInfoPath) Dim availableKb As Long -1 解析 MemAvailable 字段Linux 3.14 内核支持 For Each line As String In memInfoLines If line.StartsWith(MemAvailable:) Then Dim parts As String() line.Split(New Char() { c}, StringSplitOptions.RemoveEmptyEntries) If parts.Length 2 AndAlso Long.TryParse(parts(1), availableKb) Then Exit For End If End If Next 如果找不到 MemAvailable尝试使用 MemFree Buffers Cached If availableKb -1 Then Console.WriteLine(警告: 未找到 MemAvailable尝试计算可用内存) availableKb CalculateAvailableMemoryFromMeminfo(memInfoLines) End If If availableKb -1 Then Dim availableMB As Double availableKb / 1024.0 Return Math.Round(availableMB, 2) Else Console.WriteLine(错误: 无法解析内存信息) Return -1 End If Catch ex As UnauthorizedAccessException Console.WriteLine($错误: 没有权限读取 {_memInfoPath}) Return -1 Catch ex As Exception Console.WriteLine($读取内存信息异常: {ex.Message}) Return -1 End Try End Function summary 备用方法通过 MemFree Buffers Cached 计算可用内存 /summary Private Shared Function CalculateAvailableMemoryFromMeminfo(lines As String()) As Long Dim memFree As Long 0 Dim buffers As Long 0 Dim cached As Long 0 For Each line As String In lines If line.StartsWith(MemFree:) Then Dim parts line.Split(New Char() { c}, StringSplitOptions.RemoveEmptyEntries) If parts.Length 2 Then Long.TryParse(parts(1), memFree) ElseIf line.StartsWith(Buffers:) Then Dim parts line.Split(New Char() { c}, StringSplitOptions.RemoveEmptyEntries) If parts.Length 2 Then Long.TryParse(parts(1), buffers) ElseIf line.StartsWith(Cached:) Then Dim parts line.Split(New Char() { c}, StringSplitOptions.RemoveEmptyEntries) If parts.Length 2 Then Long.TryParse(parts(1), cached) End If Next If memFree 0 OrElse buffers 0 OrElse cached 0 Then Return memFree buffers cached End If Return -1 End Function summary 获取CPU负载扩展功能 /summary Public Shared Function GetCpuLoad() As Double Try 读取 /proc/loadavg 获取系统负载 Dim loadAvgContent File.ReadAllText(/proc/loadavg) Dim parts loadAvgContent.Split(New Char() { c}, StringSplitOptions.RemoveEmptyEntries) If parts.Length 3 Then Dim load1Min As Double If Double.TryParse(parts(0), load1Min) Then Return load1Min End If End If Catch ex As Exception 忽略错误返回 -1 End Try Return -1 End Function summary 获取Judge0进程状态扩展功能 /summary Public Shared Function IsJudge0Running() As Boolean Try Dim processes Process.GetProcessesByName(judge0) Return processes.Length 0 Catch Return False End Try End Function End Class2.4主程序入口Imports System Imports System.Net Imports System.Net.Http Imports System.Threading Module Program Private OJ As Judge0ServerConfig Private Const HEARTBEAT_INTERVAL_MS As Integer 10000 10秒 Private Const JUDGE0_READY_TIMEOUT_MINUTES As Integer 6 Private Const JUDGE0_PORT As Integer 2358 Sub Main(args As String()) 处理命令行参数 If args.Length 0 AndAlso args(0) --generate-config Then Config.SerializeJudge0Config() Console.WriteLine(配置文件已生成请根据需要修改后重新启动) Return End If Console.WriteLine($[{DateTime.Now}] ) Console.WriteLine($[{DateTime.Now}] AIOJ_Judge0 心跳客户端启动) Console.WriteLine($[{DateTime.Now}] 版本: 1.0.0) Console.WriteLine($[{DateTime.Now}] ) 等待 Judge0 服务就绪 If Not WaitForJudge0Ready() Then Console.WriteLine($[{DateTime.Now}] 警告: Judge0 服务未就绪继续启动心跳...) End If 读取配置 OJ Config.DeserializeJudge0Config() If OJ.OJURL Is Nothing OrElse OJ.Judge0Port -1 Then Console.WriteLine($[{DateTime.Now}] 错误: 无法读取配置文件) Console.WriteLine($[{DateTime.Now}] 请运行: {AppDomain.CurrentDomain.FriendlyName} --generate-config) Console.ReadKey() Return End If 显示配置信息 DisplayConfiguration() 开始心跳循环 RunHeartbeatLoop() Console.WriteLine($[{DateTime.Now}] 心跳客户端已停止) End Sub summary 等待 Judge0 服务就绪 /summary Private Function WaitForJudge0Ready() As Boolean Console.WriteLine($[{DateTime.Now}] 等待 Judge0 服务就绪...) Dim maxRetries As Integer (JUDGE0_READY_TIMEOUT_MINUTES * 60) / (HEARTBEAT_INTERVAL_MS / 1000) Dim retryCount As Integer 0 While retryCount maxRetries If CheckJudge0Service() Then Console.WriteLine($[{DateTime.Now}] ✓ Judge0 服务已就绪) Return True End If retryCount 1 If retryCount Mod 6 0 Then 每分钟输出一次 Console.WriteLine($[{DateTime.Now}] 等待中... ({retryCount * 10}秒/{maxRetries * 10}秒)) End If Thread.Sleep(HEARTBEAT_INTERVAL_MS) End While Console.WriteLine($[{DateTime.Now}] ✗ Judge0 服务超时{JUDGE0_READY_TIMEOUT_MINUTES}分钟) Return False End Function summary 检查 Judge0 服务是否可用 /summary Private Function CheckJudge0Service() As Boolean Try Using hc As New HttpClient() hc.Timeout TimeSpan.FromSeconds(5) Dim response As HttpResponseMessage hc.GetAsync( $http://localhost:{JUDGE0_PORT}/about ).GetAwaiter().GetResult() Return response.IsSuccessStatusCode End Using Catch ex As Exception Return False End Try End Function summary 显示当前配置 /summary Private Sub DisplayConfiguration() Console.WriteLine($[{DateTime.Now}] 配置信息:) Console.WriteLine($ - Manager地址: {String.Join(, , OJ.OJURL)}) Console.WriteLine($ - Judge0端口: {OJ.Judge0Port}) Console.WriteLine($ - 组别: {String.Join(, , OJ.Group)}) Console.WriteLine($ - 队列容量: {OJ.QueueMaxSize}) Console.WriteLine($ - 并行数: {OJ.RequestCountSize}) Console.WriteLine($ - 内存阈值: {OJ.AvailableMemoryThresholdOfMB} MB) Console.WriteLine($ - 心跳间隔: {HEARTBEAT_INTERVAL_MS / 1000}秒) End Sub summary 心跳主循环 /summary Private Sub RunHeartbeatLoop() Dim heartbeatCount As Integer 0 Dim lastMemoryLog As DateTime DateTime.MinValue While True Try heartbeatCount 1 获取系统可用内存 Dim availableMemoryMB As Double ServerMonitor.GetUbuntuAvailableMemoryMB() 内存获取失败时的处理 If availableMemoryMB 0 Then 使用上次成功获取的值或默认值 availableMemoryMB 1024 默认1GB End If 每60秒输出一次内存日志 If (DateTime.Now - lastMemoryLog).TotalSeconds 60 Then Console.WriteLine($[{DateTime.Now}] 系统可用内存: {availableMemoryMB:F2} MB) lastMemoryLog DateTime.Now 检查CPU负载可选 Dim cpuLoad As Double ServerMonitor.GetCpuLoad() If cpuLoad 0 Then Console.WriteLine($[{DateTime.Now}] 系统负载: {cpuLoad:F2}) End If End If 向所有Manager发送心跳 Dim successCount As Integer 0 For i As Integer 0 To OJ.OJURL.Length - 1 Dim url OJ.OJURL(i) Dim group OJ.Group(i) 异步发送心跳 Dim task Heartbeat.SendRequest( OJ.Judge0Port, group, url, OJ.QueueMaxSize, OJ.RequestCountSize, availableMemoryMB, OJ.AvailableMemoryThresholdOfMB, 10 ) 等待结果同步等待确保顺序 Dim result task.GetAwaiter().GetResult() If result 0 Then successCount 1 Else 仅在首次失败或错误码变化时输出日志 Console.WriteLine($[{DateTime.Now}] 心跳失败 [{url}]: 错误码 {result}) End If Next 所有心跳都失败时的特殊处理 If successCount 0 AndAlso heartbeatCount Mod 6 0 Then Console.WriteLine($[{DateTime.Now}] 警告: 所有Manager均无法连接) End If 等待下一个心跳周期 Thread.Sleep(HEARTBEAT_INTERVAL_MS) Catch ex As Exception Console.WriteLine($[{DateTime.Now}] 心跳循环异常: {ex.Message}) Thread.Sleep(HEARTBEAT_INTERVAL_MS) End Try End While End Sub End Module三、配置文件示例?xml version1.0 encodingutf-8? Judge0ServerConfig xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:xsdhttp://www.w3.org/2001/XMLSchema OJURL stringhttp://192.31.116.222:6666/string stringhttp://xxx.xxx.xxx.xxx:6666/string /OJURL Judge0Port2358/Judge0Port Group int0/int int1/int /Group QueueMaxSize100/QueueMaxSize RequestCountSize4/RequestCountSize AvailableMemoryThresholdOfMB200/AvailableMemoryThresholdOfMB /Judge0ServerConfig四、部署和运行4.1编译和发布编译得到dll将整个文件夹复制到服务器4.2启动服务# 生成配置文件首次运行 ./AIOJ_Judge0 --generate-config # 编辑配置文件 vim AIOJ_Judge0.conf # 启动心跳客户端 ./AIOJ_Judge04.3配置为系统服务[Unit] DescriptionJudge0 Heartbeat Client Afternetwork.target judge0.service [Service] Typesimple Userjudge0 WorkingDirectory/opt/judge0-heartbeat ExecStart/opt/judge0-heartbeat/AIOJ_Judge0 Restartalways RestartSec10 StandardOutputappend:/var/log/judge0-heartbeat.log StandardErrorappend:/var/log/judge0-heartbeat-error.log [Install] WantedBymulti-user.target4.4启动服务sudo systemctl daemon-reload sudo systemctl enable judge0-heartbeat sudo systemctl start judge0-heartbeat sudo systemctl status judge0-heartbeatPS如果安装.NET8运行时跑不起来可以尝试安装.NET 8 SDK

相关新闻

【Java课程设计/毕业设计】基于 SpringBoot+Vue 的校园体育赛事数据可视化系统的设计与实现 基于 SpringBoot 的大学生运动会数字化管理平台【附源码、数据库、万字文档】

【Java课程设计/毕业设计】基于 SpringBoot+Vue 的校园体育赛事数据可视化系统的设计与实现 基于 SpringBoot 的大学生运动会数字化管理平台【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/7 5:25:32 阅读更多 →
《深入理解计算机系统》计算机系统漫游

《深入理解计算机系统》计算机系统漫游

《深入理解计算机系统》(简称 CSAPP)书中 “计算机系统漫游” 部分的核心内容。这部分通常作为全书的引言或第一章,旨在宏观地描绘计算机系统的构成和运作方式。 信息的表示与处理(核心基础) 计算机系统处理的所有信息…

2026/7/7 5:23:32 阅读更多 →
远程协助软件推荐 远程协助软件哪个好

远程协助软件推荐 远程协助软件哪个好

日常帮亲友排查设备故障、异地处理工作难题,都需要靠谱的远程协助,便捷高效的远程协助能大幅降低跨设备、跨地域的操作成本。哪款远程协助软件体验比较呢?无界趣连2.0凭借轻量化设计与扎实的使用体验,成为个人日常远程协助的优选工…

2026/7/7 4:55:49 阅读更多 →

最新新闻

OceanBase发布“湖库一体”AI数据库:一套引擎替代交易库+数仓+向量库+数据湖

OceanBase发布“湖库一体”AI数据库:一套引擎替代交易库+数仓+向量库+数据湖

6月29日,OceanBase正式发布面向AI时代的湖库一体AI数据库,提出以“湖库一体”为核心架构,将数据湖的开放与海量存储能力、数据库的事务处理与分析能力,以及多模态数据处理能力统一到一套强一致的数据底座上,帮助Agent&…

2026/7/7 6:15:44 阅读更多 →
eHR 五层完整架构解析:分清基础刚需功能与高阶增值能力

eHR 五层完整架构解析:分清基础刚需功能与高阶增值能力

人力资源eHR软件系统(Electronic Human Resources System),是指通过信息化手段将企业HR管理全流程数字化、自动化的软件平台,涵盖招聘、入职、薪酬、绩效、考勤、培训等核心模块,帮助企业告别纸质档案与Excel表格&…

2026/7/7 6:13:44 阅读更多 →
Python处理气象NetCDF数据:读取、裁剪、统计与可视化

Python处理气象NetCDF数据:读取、裁剪、统计与可视化

一、前言 做气象、海洋、能源研究的同学,几乎每天都要和NetCDF(.nc)格式打交道。这种格式虽然跨平台、自带元数据,但对新手来说,用Python读取、裁剪、计算区域平均、画图,每一步都可能踩坑。本文基于公开的…

2026/7/7 6:09:43 阅读更多 →
《我的倒霉蛋宝贝》 泰剧|在线观看|奇幻|浪漫|Unlucky Bae

《我的倒霉蛋宝贝》 泰剧|在线观看|奇幻|浪漫|Unlucky Bae

《我的倒霉蛋宝贝》 泰剧|在线观看|奇幻|浪漫|Unlucky Bae资料可在线播放《我的倒霉蛋宝贝》https://tool.nineya.com/s/1jskahdln English Practice Fantasy Romance Edition 以《我的倒霉蛋宝贝》为主题的英语练习,边追剧边学英语。Part 1 Vocabulary Choose th…

2026/7/7 6:09:43 阅读更多 →
Stable Diffusion本地部署指南:免费无限量AI绘画实战

Stable Diffusion本地部署指南:免费无限量AI绘画实战

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Qwen 随心用,限时 5 折。 👉 点击领海量免费额度 最近在AI绘画领域,不少开发者都遇到了同样的问题:云端AI绘画工具要么收费昂贵,要么生成次数有限制…

2026/7/7 6:09:43 阅读更多 →
YimMenu技术架构深度解析:从游戏逆向工程到现代C++菜单设计

YimMenu技术架构深度解析:从游戏逆向工程到现代C++菜单设计

YimMenu技术架构深度解析:从游戏逆向工程到现代C菜单设计 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/Y…

2026/7/7 6:07:42 阅读更多 →

日新闻

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

图片浏览是移动应用中最高频的场景之一。从社交应用的照片流到电商平台的商品图集,从旅游应用的景点相册到摄影作品展示——用户对图片浏览的体验要求不断提高:流畅的切换动画、直观的缩略图导航、便捷的收藏操作、自动播放模式。HarmonyOS NEXT ArkUI 虽…

2026/7/7 0:05:16 阅读更多 →
24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V稳压芯片完整选型指南 PW8600 PW75XX PW2815 PW2312B LDODC/DC全方案 一、24V稳压方案概述 24V直流电源在工业自动化、门禁系统、电梯控制、汽车电子、LED驱动、监控设备等场景中应用极广,是最常见的中压直流母线电压。要将24V母线稳定降压至下游MCU、传感器…

2026/7/7 0:05:16 阅读更多 →
RAG+知识图谱混合检索与Graph RAG核心对比

RAG+知识图谱混合检索与Graph RAG核心对比

做企业RAG落地的团队,往往容易卡在一容易踩坑的选型难题: 当需求单纯靠向量RAG搞不定、单纯靠知识图谱也搞不定,必须同时依赖「文本语义理解 实体关系推理」时,到底是做「向量图谱混合检索」就够了,还是必须上「Grap…

2026/7/7 0:07:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻