OJ判题系统异常处理

环境

  • MacBook M2 PRO
  • Docker27.x
  • Java 1.8

沙箱不断输出内存信息(Docker版)

为什么在本地Main方法测试一切正常,当前端传入代码在沙箱执行时候却不断打印内存信息?

94cfba405afc10748c10728933929adc_MD5.jpeg

解决方案:关闭正在运行中的容器

当我们关闭Docker 正在运行的容器后 获取内存占用才真正完成

23522caf4eef83db86dd3967bb29c419_MD5.jpeg a3af82cfa70985d373f7a36f4bcf3c1b_MD5.jpeg
java
复制代码
@Override public void close() throws IOException { log.info("尝试关闭 获取内存占用 "); try { // 停止容器 dockerClient.stopContainerCmd(containerId).exec(); System.out.println("容器已停止:" + containerId); // 删除容器 dockerClient.removeContainerCmd(containerId).exec(); System.out.println("容器已删除:" + containerId); } catch (Exception e) { System.out.println("停止或删除容器时发生异常:" + e.getMessage()); } }

原因

当我们提交的代码在Docker容器执行完成后,但是容器仍然在运行。所以会不断的获取内存占用的信息输出。

代码沙箱执行完成后,OJ后台关键信息为空?(Docker版)

202c02fe3b030ebdb70e4ab5c1b3e68e_MD5.jpeg

解决方案: 重写getOutputResponse()

#javaDockerCodeSandbox

java
复制代码
@Override public ExecuteCodeResponse getOutputResponse(List<ExecuteMessage> executeMessageList) { JudgeInfo judgeInfo = new JudgeInfo(); ... //获取最大内存 judgeInfo.setMemory(executeMessage.getMemory()); //获取最大时间 judgeInfo.setTime(executeMessage.getTime()); //获取最大输出 judgeInfo.setMessage(executeMessage.getMessage()); .... executeCodeResponse.setJudgeInfo(judgeInfo); //完成代码看下方原因

原因

大致梳理下整体用户提交代码到返回沙箱判题结果的流程

#JudgeServiceImpl 1.封装执行代码请求信息,调用代码沙箱执行

java
复制代码
ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);

2.这里用的是远程代码沙箱,最终的请求从这里发出到代码沙箱(关键点!!!) #RemoteCodeSandbox

f56ca4edac95f8b0c6cb5e5388c42ff9_MD5.jpeg

#javaDockerCodeSandbox 3.从代码沙箱入手调试到这一步,有我们需要的 - 内存(memory) - 时间(time)

5f7a4648945a0f9f0526a51c7c25a103_MD5.jpeg
  • 思考:既然代码沙箱执行完成后,有我们需要的信息。为什么在OJ后台却为空呢?

答案就在明面上,在封装代码沙箱返回到OJ后台的信息并不完整

4.代码沙箱执行完成后我们回到OJ后台查看,关键信息都是没有的

a2507fae89cb745c04266927406dc311_MD5.jpeg

顺藤摸瓜,这些信息从哪里封装的? #javaDockerCodeSandbox

使用了模版方法优化,但只重写runFile()方法所以封装沙箱结果还用的是

#javaDockerCodeSandboxTemplate 的getOutputResponse()

源赖氏妹封装啊!

9017f7fd6ac934b9422b3fdc942179fc_MD5.jpeg

后面拓展实现其他沙箱获取内存的方式可能不相同, 作者选择重写getOutputResponse()

java
复制代码
/** * 5.获取输出结果 * * @param executeMessageList * @return {@link ExecuteCodeResponse } */@Override public ExecuteCodeResponse getOutputResponse(List<ExecuteMessage> executeMessageList) { JudgeInfo judgeInfo = new JudgeInfo(); ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse(); List<String> outputList = new ArrayList<>(); //取用时最大值,便于判断是否超时 long maxTime = 0; for (ExecuteMessage executeMessage : executeMessageList) { String errorMessage = executeMessage.getErrorMessage(); if (StrUtil.isNotBlank(errorMessage)) { executeCodeResponse.setMessage(errorMessage); // 用户提交的代码执行中存在错误 // todo 设置枚举值 executeCodeResponse.setStatus(3); break; } outputList.add(executeMessage.getMessage()); Long time = executeMessage.getTime(); if (time != null) { maxTime = Math.max(maxTime, time); } //获取最大内存 judgeInfo.setMemory(executeMessage.getMemory()); //获取最大时间 judgeInfo.setTime(executeMessage.getTime()); //获取最大输出 judgeInfo.setMessage(executeMessage.getMessage()); } // 正常运行完成 if (outputList.size() == executeMessageList.size()) { // todo 设置枚举值 executeCodeResponse.setStatus(1); } executeCodeResponse.setOutputList(outputList); executeCodeResponse.setJudgeInfo(judgeInfo); return executeCodeResponse; }

最后

非常感谢您读到这里!希望本文为您提供了有价值的信息。 如果有任何疑问或者建议,欢迎在评论区留言 期待大家的宝贵意见!

我是Sion ,代码世界的茄咧啡。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
Sion
下载 APP