当前位置: 首页 > news >正文

微服务的编程测评系统17-判题功能-代码沙箱

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 1. 判题功能
    • 1.1 初版代码-friend-api
    • 1.2 初版代码-judge-无执行代码
    • 1.3 初版代码-judge-执行代码
    • 1.4 创建目录
    • 1.5 拉取镜像-创建容器
    • 1.6 编译代码
  • 总结


前言

1. 判题功能

1.1 初版代码-friend-api

在friend中

    @PostMapping("/submitQuestion")public R<SubmitQuestionVO> submitQuestion(@RequestBody SubmitQuestionDTO submitQuestionDTO){log.info("用户提交题目代码,submitQuestionDTO:{}",submitQuestionDTO);return R.ok(userQuestionService.submitQuestion(submitQuestionDTO));}
@Service
public class UserQuestionService implements IUserQuestionService {@Overridepublic SubmitQuestionVO submitQuestion(SubmitQuestionDTO submitQuestionDTO) {Integer programType = submitQuestionDTO.getProgramType();if(ProgramType.JAVA.getValue().equals(programType)){}throw new ServiceException(ResultCode.PROGRAM_TYPE_ERR);}
}

然后submitQuestion要调用judge服务,就去调用api
把friend的参数传给judge,调用judge服务
在api中
创建一个模块oj-api

        <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency><dependency><groupId>com.bite</groupId><artifactId>oj-common-core</artifactId><version>1.0-SNAPSHOT</version></dependency>
@FeignClient(contextId = "RemoteJudgeService", value = Constants.JUDGE_SERVICE)
public interface RemoteJudgeService {@PostMapping("/judge/doJudgeJavaCode")R<UserQuestionResultVO> doJudgeJavaCode(@RequestBody JudgeSubmitDTO judgeSubmitDTO);
}

创建一个service来调用judge
然后vo和dto都定义在api中

@Getter
@Setter
public class JudgeSubmitDTO {private Long userId;private Long examId;//编程语言类型(0 java 1 C++)private Integer programType;private Long questionId;//题目难度private Integer difficulty;//时间限制 msprivate Long timeLimit;//空间限制 kbprivate Long spaceLimit;//拼装完整的用户代码  用户提交的代码 + main函数private String userCode;//输入数据private List<String> inputList;//期望输出数据private List<String> outputList;
}
@Getter
@Setter
public class UserQuestionResultVO {//是够通过标识private Integer pass; // 0  未通过  1 通过private String exeMessage; //异常信息private List<UserExeResult> userExeResultList;@JsonIgnoreprivate Integer score;
}
@Getter
@Setter
public class UserExeResult {private String input;private String output;   //期望输出private String exeOutput; //实际输出
}

然后在friend中调用

@Overridepublic R<UserQuestionResultVO> submitQuestion(SubmitQuestionDTO submitQuestionDTO) {Integer programType = submitQuestionDTO.getProgramType();if(ProgramType.JAVA.getValue().equals(programType)){JudgeSubmitDTO judgeSubmitDTO = makeJudgeSubmitDTO(submitQuestionDTO);return remoteJudgeService.doJudgeJavaCode(judgeSubmitDTO);}throw new ServiceException(ResultCode.PROGRAM_TYPE_ERR);}private JudgeSubmitDTO makeJudgeSubmitDTO(SubmitQuestionDTO submitQuestionDTO) {JudgeSubmitDTO judgeSubmitDTO =new JudgeSubmitDTO();Long questionId = submitQuestionDTO.getQuestionId();QuestionES questionES = questionRepository.findById(questionId).orElse(null);if(questionES!=null){BeanUtil.copyProperties(questionES,judgeSubmitDTO);}else{Question question = questionMapper.selectById(questionId);BeanUtil.copyProperties(question,judgeSubmitDTO);BeanUtil.copyProperties(question,questionES);questionRepository.save(questionES);}judgeSubmitDTO.setUserId(ThreadLocalUtil.get(Constants.USER_ID,Long.class));judgeSubmitDTO.setProgramType(submitQuestionDTO.getProgramType());judgeSubmitDTO.setUserCode(codeConnect(submitQuestionDTO.getUserCode(),questionES.getMainFuc()));List<QuestionCase> questionCaseList = JSONUtil.toList(questionES.getQuestionCase(), QuestionCase.class);List<String> inputList = questionCaseList.stream().map(QuestionCase::getInput).toList();List<String> outputList = questionCaseList.stream().map(QuestionCase::getOutput).toList();judgeSubmitDTO.setInputList(inputList);judgeSubmitDTO.setOutputList(outputList);return judgeSubmitDTO;}private String codeConnect(String userCode, String mainFunc) {String targetCharacter = "}";int targetLastIndex = userCode.lastIndexOf(targetCharacter);if (targetLastIndex != -1) {return userCode.substring(0, targetLastIndex) + "\n" + mainFunc + "\n" + userCode.substring(targetLastIndex);}throw new ServiceException(ResultCode.FAILED);}
}
@Getter
@Setter
public class QuestionCase {private String input;private String output;
}

记得还要给friend的启动类加上

@EnableFeignClients(basePackages = "com.ck.api")

不然找不到

1.2 初版代码-judge-无执行代码

我们用docker来执行java代码,就是代码沙箱–》隔离代码环境

        <dependency><groupId>com.ck</groupId><artifactId>oj-api</artifactId><version>1.0-SNAPSHOT</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><dependency><groupId>com.github.docker-java</groupId><artifactId>docker-java</artifactId><version>3.3.4</version></dependency>

com.github.docker-java就是代码沙箱

    @PostMapping("/judge/doJudgeJavaCode")R<UserQuestionResultVO> doJudgeJavaCode(@RequestBody JudgeSubmitDTO judgeSubmitDTO){return R.ok(judgeService.doJudgeJavaCode(judgeSubmitDTO));}
@Service
public class JudgeService implements IJudgeService {@Autowiredprivate ISandBoxService sandBoxService;@Overridepublic UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {SandBoxExecuteResult sandBoxExecuteResult = sandBoxService.exeJavaCode(judgeSubmitDTO.getUserCode(),judgeSubmitDTO.getInputList());return null;}
}

代码的执行我们用的是sandBoxService来执行,就是用代码沙箱
这个方法exeJavaCode的实现还不着急


@Data
public class SandBoxExecuteResult {private CodeRunStatus runStatus;  //执行结果private String exeMessage;   //异常信息private List<String> outputList; //执行结果private Long useMemory;  //占用内存  kbprivate Long useTime;   //消耗时间   mspublic static SandBoxExecuteResult fail(CodeRunStatus runStatus, String errorMsg) {SandBoxExecuteResult result = new SandBoxExecuteResult();result.setRunStatus(runStatus);result.setExeMessage(errorMsg);return result;}public static SandBoxExecuteResult fail(CodeRunStatus runStatus) {SandBoxExecuteResult result = new SandBoxExecuteResult();result.setRunStatus(runStatus);result.setExeMessage(runStatus.getMsg());return result;}public static SandBoxExecuteResult fail(CodeRunStatus runStatus, List<String> outputList,Long useMemory, Long useTime) {SandBoxExecuteResult result = new SandBoxExecuteResult();result.setRunStatus(runStatus);result.setExeMessage(runStatus.getMsg());result.setOutputList(outputList);result.setUseMemory(useMemory);result.setUseTime(useTime);return result;}public static SandBoxExecuteResult success(CodeRunStatus runStatus, List<String> outputList,Long useMemory, Long useTime) {SandBoxExecuteResult result = new SandBoxExecuteResult();result.setRunStatus(runStatus);result.setOutputList(outputList);result.setUseMemory(useMemory);result.setUseTime(useTime);return result;}
}

@Getter
public enum CodeRunStatus {RUNNING(1, "运行中"),SUCCEED(2, "运行成功"),FAILED(3, "运行失败"),NOT_ALL_PASSED(4, "未通过所有用例"),UNKNOWN_FAILED(5, "未知异常,请您稍后重试"),COMPILE_FAILED(6, "编译失败"),OUT_OF_MEMORY(7, "运行结果正确,但是超出空间限制"),OUT_OF_TIME(8, "运行结果正确,但是超出时间限制");private Integer value;private String msg;CodeRunStatus(Integer value, String msg) {this.value = value;this.msg = msg;}
}

然后就是继续完善doJudgeJavaCode方法了


public class JudgeConstants {public static final String ERROR_ANSWER = "未完全通过所有用例";public static final String OUT_OF_MEMORY = "超出内存限制,请优化代码";public static final String OUT_OF_TIME = "运行时间超出限制,请优化代码";public static final Integer ERROR_SCORE = 0;public static final Integer DEFAULT_SCORE = 100;public static final String EXAM_CODE_DIR = "user-code";public static final String CODE_DIR_POOL = "user-code-pool";public static final String DOCKER_USER_CODE_DIR = "/usr/share/java";public static final String USER_CODE_JAVA_CLASS_NAME = "Solution.java";public static final String USER_CODE_JAVA_FILE_NAME = "Solution";public static final String JAVA_ENV_IMAGE = "openjdk:8-jdk-alpine";public static final String JAVA_CONTAINER_PREFIX = "/";public static final String JAVA_CONTAINER_NAME = "oj-jdk";public static final String[] DOCKER_JAVAC_CMD = new String[] {"javac", "/usr/share/java/Solution.java"};public static final String[] DOCKER_JAVA_EXEC_CMD = new String[]{"java", "-cp", DOCKER_USER_CODE_DIR, USER_CODE_JAVA_FILE_NAME};//  java -cp  /usr/share/java  Solution 1 2}

@Service
public class JudgeService implements IJudgeService {@Autowiredprivate ISandBoxService sandBoxService;@Autowiredprivate UserSubmitMapper userSubmitMapper;@Overridepublic UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {SandBoxExecuteResult sandBoxExecuteResult =sandBoxService.exeJavaCode(judgeSubmitDTO.getUserCode(),judgeSubmitDTO.getInputList());UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();if(sandBoxExecuteResult!=null &&sandBoxExecuteResult.getRunStatus().getValue().equals(CodeRunStatus.SUCCEED.getValue())){List<String> exeOutputList = sandBoxExecuteResult.getOutputList();List<String> outputList = judgeSubmitDTO.getOutputList();if(exeOutputList.size()!=outputList.size()){whenOutPutListErr(userQuestionResultVO);}else {boolean flag = setUserExeResultList(judgeSubmitDTO, exeOutputList, outputList, userQuestionResultVO);if(!flag){whenOutPutListErr(userQuestionResultVO);}else{//每个数量都对得上if(sandBoxExecuteResult.getUseTime()>judgeSubmitDTO.getTimeLimit()){//时间超时whenRunTimeErr(userQuestionResultVO);}else if (sandBoxExecuteResult.getUseMemory()>judgeSubmitDTO.getSpaceLimit()){//空间跃出whenSpaceErr(userQuestionResultVO);}else {//代码正确了whenRunSucess(judgeSubmitDTO, userQuestionResultVO);}}}}else{whenRunErr(userQuestionResultVO, sandBoxExecuteResult);}insertUserSubmit(judgeSubmitDTO, userQuestionResultVO);return userQuestionResultVO;}private static void whenRunErr(UserQuestionResultVO userQuestionResultVO, SandBoxExecuteResult sandBoxExecuteResult) {userQuestionResultVO.setPass(Constants.FALSE);userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);if(sandBoxExecuteResult !=null){userQuestionResultVO.setExeMessage(sandBoxExecuteResult.getExeMessage());}else{userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMsg());}}private static void whenRunSucess(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {userQuestionResultVO.setPass(Constants.TRUE);userQuestionResultVO.setScore(JudgeConstants.DEFAULT_SCORE* judgeSubmitDTO.getDifficulty());}private static void whenSpaceErr(UserQuestionResultVO userQuestionResultVO) {userQuestionResultVO.setPass(Constants.FALSE);userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_MEMORY.getMsg());}private static void whenRunTimeErr(UserQuestionResultVO userQuestionResultVO) {userQuestionResultVO.setPass(Constants.FALSE);userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);userQuestionResultVO.setExeMessage(CodeRunStatus.OUT_OF_TIME.getMsg());}private static boolean setUserExeResultList(JudgeSubmitDTO judgeSubmitDTO, List<String> exeOutputList, List<String> outputList, UserQuestionResultVO userQuestionResultVO) {boolean flag =true;List<UserExeResult> userExeResultList = new ArrayList<>();for (int index = 0; index < exeOutputList.size(); index++) {UserExeResult userExeResult = new UserExeResult();userExeResult.setExeOutput(exeOutputList.get(index));userExeResult.setOutput(outputList.get(index));userExeResult.setInput(judgeSubmitDTO.getInputList().get(index));userExeResultList.add(userExeResult);if(!exeOutputList.get(index).equals(outputList.get(index))){flag=false;}}userQuestionResultVO.setUserExeResultList(userExeResultList);return flag;}private static void whenOutPutListErr(UserQuestionResultVO userQuestionResultVO) {userQuestionResultVO.setPass(Constants.FALSE);userQuestionResultVO.setScore(JudgeConstants.ERROR_SCORE);userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMsg());}private void insertUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {UserSubmit userSubmit = new UserSubmit();BeanUtil.copyProperties(userQuestionResultVO,userSubmit);BeanUtil.copyProperties(judgeSubmitDTO,userSubmit);//不同名的属性则会被保留userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>().eq(UserSubmit::getUserId,userSubmit.getUserId()).eq(UserSubmit::getQuestionId,userSubmit.getQuestionId()).isNull(userSubmit.getExamId()==null,UserSubmit::getExamId).eq(userSubmit.getExamId()!=null,UserSubmit::getExamId,userSubmit.getExamId()));userSubmitMapper.insert(userSubmit);}}

这样就成功了

1.3 初版代码-judge-执行代码

        <dependency><groupId>com.github.docker-java</groupId><artifactId>docker-java</artifactId><version>3.3.4</version></dependency>

docker来实现代码沙箱
要用到javac和java指令–.对文件操作–》userCode变为文件—》宿主机和容器挂载,同步文件

我们把文件存在user-code文件夹下
在这里插入图片描述
然后添加到gitignore

1.4 创建目录

    private String userCodeDir;private String userCodeFileName;//创建并返回用户代码的文件private void createUserCodeFile(Long userId, String userCode) {String examCodeDir = System.getProperty("user.dir")+File.separator+ JudgeConstants.EXAM_CODE_DIR;if(!FileUtil.exist(examCodeDir)){FileUtil.mkdir(examCodeDir);}String time = LocalDateTimeUtil.format(LocalDateTime.now(), DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));userCodeDir = examCodeDir +File.separator +userId + Constants.UNDERLINE_SEPARATOR +time;userCodeFileName = userCodeDir+File.separator +JudgeConstants.USER_CODE_JAVA_CLASS_NAME;FileUtil.writeString(userCode,userCodeFileName,Constants.UTF8);}

这个代码也是会自动创建目录的
userCodeDir是一个二级目录,用来区分是哪个用户提交的,什么时间提交的
然后userCodeDir里面有一个文件就是Solution.java

    public static final String UNDERLINE_SEPARATOR = "_";/*** UTF-8 字符集*/public static final String UTF8 = "UTF-8";

examCodeDir就是一级目录
反正这个方法就是来创建对应的文件的
然后就是操作docker了

1.5 拉取镜像-创建容器

    private void initDockerSanBox() {DefaultDockerClientConfig clientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder().withDockerHost(dockerHost).build();dockerClient = DockerClientBuilder.getInstance(clientConfig).withDockerCmdExecFactory(new NettyDockerCmdExecFactory()).build();//拉取镜像pullJavaEnvImage();//创建容器  限制资源   控制权限HostConfig hostConfig = getHostConfig();CreateContainerCmd containerCmd = dockerClient.createContainerCmd(JudgeConstants.JAVA_ENV_IMAGE).withName(JudgeConstants.JAVA_CONTAINER_NAME);CreateContainerResponse createContainerResponse = containerCmd.withHostConfig(hostConfig).withAttachStderr(true).withAttachStdout(true).withTty(true).exec();//记录容器idcontainerId = createContainerResponse.getId();//启动容器dockerClient.startContainerCmd(containerId).exec();}

要操作docker,首先要和docker建立连接
dockerHost就是docker的地址

    @Value("${sandbox.docker.host:tcp://localhost:2375}")private String dockerHost;

创建clientConfig建立连接
然后创建dockerClient去操作docker

    private DockerClient dockerClient;
        //拉取镜像pullJavaEnvImage();

的意思就是拉取java环境的镜像

    //拉取java执行环境镜像 需要控制只拉取一次private void pullJavaEnvImage() {ListImagesCmd listImagesCmd = dockerClient.listImagesCmd();List<Image> imageList = listImagesCmd.exec();for (Image image : imageList) {String[] repoTags = image.getRepoTags();if (repoTags != null && repoTags.length > 0 && JudgeConstants.JAVA_ENV_IMAGE.equals(repoTags[0])) {return;}}PullImageCmd pullImageCmd = dockerClient.pullImageCmd(JudgeConstants.JAVA_ENV_IMAGE);try {pullImageCmd.exec(new PullImageResultCallback()).awaitCompletion();} catch (InterruptedException e) {throw new RuntimeException(e);}}

比如要安装jdk的镜像

    public static final String JAVA_ENV_IMAGE = "openjdk:8-jdk-alpine";

拉取这个就可以跑java代码了

ListImagesCmd listImagesCmd = dockerClient.listImagesCmd();

这个是获取docker里面所有的镜像
因为可能已经安装了jdk的镜像1,就不用再次安装了

        for (Image image : imageList) {String[] repoTags = image.getRepoTags();if (repoTags != null && repoTags.length > 0 && JudgeConstants.JAVA_ENV_IMAGE.equals(repoTags[0])) {return;}}

这个意思就是有了java8的镜像就不用拉取镜像了
就相当于docker ps -a

        PullImageCmd pullImageCmd = dockerClient.pullImageCmd(JudgeConstants.JAVA_ENV_IMAGE);

这个就是拉取镜像的意思,就是获取拉取镜像的指令

            pullImageCmd.exec(new PullImageResultCallback()).awaitCompletion();

这个exec就是执行指令的意思
PullImageResultCallback参数的作用就是监听拉取镜像的过程
这个可以获取拉取镜像的进度状态等等
awaitCompletion方法的作用是,阻塞调用的意思,就是要在这里等待拉取镜像结束
然后继续回到方法initDockerSanBox

    private String containerId;
        //创建容器  限制资源   控制权限HostConfig hostConfig = getHostConfig();CreateContainerCmd containerCmd = dockerClient.createContainerCmd(JudgeConstants.JAVA_ENV_IMAGE).withName(JudgeConstants.JAVA_CONTAINER_NAME);CreateContainerResponse createContainerResponse = containerCmd.withHostConfig(hostConfig).withAttachStderr(true).withAttachStdout(true).withTty(true).exec();

这个就是创建容器的过程
这个containerCmd 是先创建指令,然后exec来执行
JAVA_ENV_IMAGE是镜像名字
public static final String JAVA_CONTAINER_NAME = “oj-jdk”;这个是容器的名字
在getHostConfig方法中

    //限制资源   控制权限private HostConfig getHostConfig() {HostConfig hostConfig = new HostConfig();//设置挂载目录,指定用户代码路径hostConfig.setBinds(new Bind(userCodeDir, new Volume(JudgeConstants.DOCKER_USER_CODE_DIR)));//限制docker容器使用资源hostConfig.withMemory(memoryLimit);hostConfig.withMemorySwap(memorySwapLimit);hostConfig.withCpuCount(cpuLimit);hostConfig.withNetworkMode("none");  //禁用网络hostConfig.withReadonlyRootfs(true); //禁止在root目录写文件return hostConfig;}

setBinds是设置挂载目录
容器要操作外面文件的代码–》只能挂载了
后面的设置就是资源的设置了
memoryLimit这些都是nacos的配置

    @Value("${sandbox.limit.memory:100000000}")private Long memoryLimit;@Value("${sandbox.limit.memory-swap:100000000}")private Long memorySwapLimit;@Value("${sandbox.limit.cpu:1}")private Long cpuLimit;

getHostConfig就是很重要的设置了
withAttachStderr和withAttachStdout就是标准错误输出和标准输出
withTty就是为容器配置一个伪终端
因为容器是要执行一些指令的,只有有这个伪终端,容器才会去执行相关指令
exec就是执行创建容器的指令
最后回到主方法exeJavaCode
我们就要开始编译代码了

1.6 编译代码

    //编译//的使用docker编译private CompileResult compileCodeByDocker() {String cmdId = createExecCmd(JudgeConstants.DOCKER_JAVAC_CMD, null, containerId);DockerStartResultCallback resultCallback = new DockerStartResultCallback();CompileResult compileResult = new CompileResult();try {dockerClient.execStartCmd(cmdId).exec(resultCallback).awaitCompletion();if (CodeRunStatus.FAILED.equals(resultCallback.getCodeRunStatus())) {compileResult.setCompiled(false);compileResult.setExeMessage(resultCallback.getErrorMessage());} else {compileResult.setCompiled(true);}return compileResult;} catch (InterruptedException e) {//此处可以直接抛出 已做统一异常处理  也可再做定制化处理throw new RuntimeException(e);}}private String createExecCmd(String[] javaCmdArr, String inputArgs, String containerId) {if (!StrUtil.isEmpty(inputArgs)) {//当入参不为空时拼接入参String[] inputArray = inputArgs.split(" "); //入参javaCmdArr = ArrayUtil.append(JudgeConstants.DOCKER_JAVA_EXEC_CMD, inputArray);}ExecCreateCmdResponse cmdResponse = dockerClient.execCreateCmd(containerId).withCmd(javaCmdArr).withAttachStderr(true).withAttachStdin(true).withAttachStdout(true).exec();return cmdResponse.getId();}
@Getter
@Setter
@Slf4j
public class DockerStartResultCallback extends ExecStartResultCallback {private CodeRunStatus codeRunStatus;  //记录执行成功还是失败private String errorMessage;private String message;@Overridepublic void onNext(Frame frame) {StreamType streamType = frame.getStreamType();if (StreamType.STDERR.equals(streamType)) {if (StrUtil.isEmpty(errorMessage)) {errorMessage = new String(frame.getPayload());} else {errorMessage = errorMessage + new String(frame.getPayload());}codeRunStatus = CodeRunStatus.FAILED;} else {String msgTmp = new String(frame.getPayload());if (StrUtil.isNotEmpty(msgTmp)) {message = new String(frame.getPayload());}codeRunStatus = CodeRunStatus.SUCCEED;}super.onNext(frame);}
}
@Data
public class CompileResult {private boolean compiled;  //编译是否成功private String exeMessage;  //编译输出信息 (错误信息)
}

createExecCmd就是创建编译指令
createExecCmd方法中

    public static final String[] DOCKER_JAVAC_CMD = new String[] {"javac", "/usr/share/java/Solution.java"};

第一个参数是一个数组
javac是编译的指令
后面的就是java文件
因为挂载了
所以Solution.java就会在容器的/usr/share/java/目录下
第二个参数就是入参,编译的时候没有入参
第三个参数是容器的Id
继续在compileCodeByDocker
DockerStartResultCallback继承ExecStartResultCallback,这个是会监听命令执行过程–》因为编译也会出错的,所以得监听
DockerStartResultCallback里面的onNext方法的作用是docker引擎会把指令执行输出的结果转化为Frame,不管是正确还是失败
所以onNext就可以获取正确或者错误的输出了
因为错误的信息可能会很多
所以我们用 errorMessage = errorMessage + new String(frame.getPayload());
来累加错误

            dockerClient.execStartCmd(cmdId).exec(resultCallback).awaitCompletion();

意思就是执行指令cmdId,resultCallback监听,awaitCompletion阻塞等待完成
在这里插入图片描述
这样编译就完成了
然后是对编译结果的判断

        if(!compileResult.isCompiled()){//清空场地--.清空代码文件deleteContainer();deleteUserCodeFile();return SandBoxExecuteResult.fail(CodeRunStatus.COMPILE_FAILED,compileResult.getExeMessage());}
    private void deleteUserCodeFile() {FileUtil.del(userCodeDir);}
    private void deleteContainer() {//执行完成之后删除容器dockerClient.stopContainerCmd(containerId).exec();dockerClient.removeContainerCmd(containerId).exec();//断开和docker连接try {dockerClient.close();} catch (IOException e) {throw new RuntimeException(e);}}

分别就是容器和文件的清理,必须要清理,不然会一直在那里浪费空间

总结

http://www.dtcms.com/a/354216.html

相关文章:

  • 除自身以外数组的乘积是什么意思
  • 算法刷题常见错误
  • Linux 打包及压缩基础知识总结
  • 车间生产管理遇到的问题及改善方案有哪些?
  • 在 Windows 上部署 Go 语言开发环境
  • Go语言与Docker 开发的核心应用领域
  • 源码分析unexpected EOF on client connection with an open transaction
  • 分治法——二分答案
  • 深入探索Vue:前端开发的强大框架
  • Android10 音频系统之AudioPlaybackConfiguration
  • JVM之CMS、G1|ZGC详解以及选型对比
  • SynClub-百度在海外推出的AI社交产品
  • A-Level物理课程全解析:知识点、学习计划与培训机构推荐
  • 网络编程-连接、发送、接收数据学习
  • React Hooks 完全指南:从基础到高级的实战技巧
  • C++ 由 std::thread 初始化想到的
  • TencentOS Server 4.4 下创建mysql容器无法正常运行的问题
  • wireshark解析FLV插件分享
  • 嵌入式Linux(Exynos 4412)笔记
  • 3459. 最长 V 形对角线段的长度
  • 设计模式理解
  • Nishang PowerShell工具:原理详解+使用方法+渗透实战
  • Go+Gdal 完成高性能GIS数据空间分析
  • 深度学习:常用的损失函数的使用
  • “java简单吗?”Java的“简单”与PHP的挑战:编程语言哲学-优雅草卓伊凡
  • 白话FNN、RNN、Attention和self-attention等
  • 《从有限元到深度学习:我的金属疲劳研究进阶之路》
  • 反内卷加速全产业链价值重塑 通威股份等行业龙头或率先受益
  • 基于 C# OpenCVSharp 的模板匹配检测技术方案
  • 计算机日常答疑,一起寻找问题的最优解