AcCode核心思路

文章目录

  • 在线OJ项目核心思路
    • 1. 项目介绍
    • 2.预备知识
      • 理解多进程编程
      • 为啥采用多进程而不使用多线程?
      • 标准输入&标准输出&标准错误
    • 3.项目实现
      • 题目API实现
        • 相关实体类定义
        • 新增/修改题目
        • 获取题目列表
      • 编译运行
        • 编译运行流程
    • 4.统一功能处理


在线OJ项目核心思路

在这里插入图片描述

1. 项目介绍

该项目是一个类似于力扣的在线OJ平台,可以进行题目的编写和提交编译运行以及结果展示,使用的技术栈有:Java、MySQL、SpringBoot、MyBatis、Redis、Nginx、Docker

主要功能如下:

  1. 登录和注册(Session持久化+密码加盐)
  2. 图形验证码验证登录
  3. 题目管理(题目的添加和修改)
  4. 题目提交(编译+运行)
  5. 题目编译/运行结果展示
  6. Nginx+Docker实现负载均衡

在这里插入图片描述

2.预备知识

理解多进程编程

什么是进程?

进程可以看做操作系统中一个正在运行的程序的一个抽象,也可以把进程看做是程序的一次运行过程。在操作系统内部,进程是操作系统进行资源分配的基本单位

  • 使用 PCB(进程控制块) 描述进程

  • 组织:使用一定的数据结构来组织,常见做法就是使用双向链表

  • 进程之间是相互独立的

什么是多进程?

一个CPU运行多个进程

由于CPU的运行速度极快,虽然CPU在一直进行切换,但是咱们坐在电脑前的用户,是感知不到这个切换过程的

进程和线程的关系

  1. 进程是包含线程的,一个进程里可以有一个线程,也可以有多个线程
  2. 每个进程都有独立的内存空间(虚拟地址空间),同一个进程的多个线程之间,共用这个虚拟地址空间
  3. 进程是操作系统分配资源的基本单位,线程是操作系统调度执行的基本单位
  4. 如果一个进程挂了, 不会影响到其他进程. 如果一个线程挂了, 则整个进程都要异常终止.
  5. 进程更重量, 线程更轻量. 创建/销毁/调度线程比进程更高效.

Java中的多进程编程

Java中中对系统提供的进程创建、进程终止、进程程序替换、进程间通信进程了限制,最终只给用户提供了两个操作

进程的创建

创建出一个新的进程,让这个新的进程来执行一系列任务,被创建出来的进程,称为"子进程",创建子进程的进程,称为"父进程",服务器的进程就相当于一个父进程

根据收到的用户发送过来的代码再 创建出一个子进程,一个父进程,可以有多个子进程,但是一个子进程,只能有一个父进程

为啥采用多进程而不使用多线程?

一个操作系统上是运行了很多进程的,因为进程之间是相互隔离的,一个进程挂了是不会影响到其它进程的。如果使用多线程,我们并不知道用户提交的会提交什么样的代码,很可能提交一些恶意代码导致线程崩溃,而线程挂了很有可能就影响到了我们的整个服务进程。所以一定要采用多进程而不是多线程。

标准输入&标准输出&标准错误

java和javac是一个控制台程序,它的输出,是输出到“标准输出”和"标准错误"这两个特殊的文件当中的,一个进程启动的时候,就会自动打开三个文件:

  1. 标准输入,对应到键盘
  2. 标准输出,对应到显示器
  3. 标椎错误,对应到显示器

Runtime是Java中内置的一个单例类

  • 通过runtime.exec方法参数是一个字符串,表示一个可执行程序的路径,执行这个方法就会把指定路径的可执行程序,创建出一个子进程并执行。
  • runtime.exec()方法返回的是一个Process类,表示的就是一个子进程,后续通过这个子进程来进行操作
    • 获取标准输入:process.getInputStream():该方法能把process这个子进程的标准输出给读取出来
    • 获取标准错误:process.getErrorStream():该方法能把process这个子进程的标准错误给读取出来
    • 进程等待: process.waitFor():该方法能能让主进程进行阻塞等待,等待子进程process执行完毕。

3.项目实现

题目API实现

相关实体类定义

题目实体类

public class Problem {private Integer id;private String title;private String levels;private String description;private String templateCode;private String testCode;private Date createTime;private Date updateTime;
}
新增/修改题目

新增修改题目通过判断url中的querystr里是否存在题目Id,来判断是修改题目还是新增题目

约定请求:

post
{"id" : "","title" : "题目标题","levels" : "题目难度","description" : "题干","templateCode" : "题目代码模板","testCode" :  "题目测试用例"
}

响应:

{code : 200,message : ""data: 
}
@PostMapping("/add")
public Response add(@RequestBody Problem problem) {if (problem == null || problem.getTitle() == null || "".equals(problem.getTitle().trim()) || problem.getLevels() == null ||"".equals(problem.getLevels().trim()) || problem.getTestCode() == null || "".equals(problem.getTestCode().trim()) ||problem.getTemplateCode() == null || "".equals(problem.getTemplateCode().trim())) {return Response.fail("题目参数不完整");}int ret = problemService.add(problem);if (ret == 1) {return Response.success(200,"添加成功");}return Response.fail("添加失败");
}
获取题目列表

请求:

post
{/problem/all
}

响应:

{code : 200,message:"",data:[{id : 1,title: "两数之和",levels: "简单",description: "题干",template: "题目模板"}]
}

编译运行

通过Answer表示编译运行结果,约定:

  • 错误码为0表示运行成功
  • 错误码为1表示编译错误
  • 错误码为2表示运行错误
  • 错误码为1表示提交了违规代码
public class Answer {// 错误码 0表示运行成功,1表示编译错误,2表示运行错误,-1表示违规代码private Integer errorCode;// 标准输出private String stdout;// 错误信息private String errorInfo;
}

Task类描述的是每一次代码的提交:

通过UUID生成唯一的目录,保证每个用户提交的代码相互隔离

public class Task {// 存放临时文件目录private String workDir;// 运行文件路径private String className;// 编译文件路径private String classFile;// 存放编译错误信息文件private String compileErrorFile;// 标准输出文件private String stdoutFile;// 标准错误文件private String stderrFile;public Task() {this.workDir = "./tmp/"+UUID.randomUUID().toString()+"/";this.className = "Solution";this.classFile = workDir+ "Solution.java";this.compileErrorFile = workDir+"compileErrInfo.txt";this.stdoutFile = workDir+"stdout.txt";this.stderrFile = workDir+"stderr.txt";}
}
编译运行流程

请求:

{problemId : "题目id",code : "提交的代码"
}

响应:

{code : 200,message : "信息",data:{errorCode : "错误码",stdout: "标准输出",derrorInfo, "出错信息"}
}

编译运行流程:

  1. 对用户提交代码进行判空
  2. 从数据库中查询出测试用例进和提交代码进行拼接,形成完整代码。
  3. 对用户提交代码进行安全校验,判断其是否提交操作系统命令、文件网络等危险操作代码
  4. 把拼接好的代码写入到对应文件
  5. 进行编译和运行。

如下方法表示一次编译或者运行:

  • 通过判断stdoutFile是否为空来判断是编译还是运行
  • 从子进程process中的标准错误流中读取数据写入到task类的唯一的编译错误信息文件中,再判断文件内容是否为空
  • 如果编译错误信息文件不为空,说明编译出错直接返回
  • 如果编译错误信息文件为空,说明编译正确,再对编译后的字节码进行运行
  • 运行后再进行判断标准错误信息文件是否为空,如果为空说明运行正常,读取到标准输入文件里的信息返回给用户
/*** 编译运行* @param cmd 执行的命令* @param stdoutFile* @param stderrFile* @return*/
public static int run(String cmd,String stdoutFile,String stderrFile) {Runtime runtime =  Runtime.getRuntime();int exitCode = -1;try {// 执行命令获得子进程Process process = runtime.exec(cmd);// 编译if (stdoutFile == null) {try (InputStream stderrInoutStream = process.getErrorStream();OutputStream stderrOutputSteam = new FileOutputStream(stderrFile);){int ch;// 将错误信息读入到错误日志文件while ((ch = stderrInoutStream.read()) != -1) {stderrOutputSteam.write(ch);}}}// 说明是运行if (stdoutFile != null) {try (InputStream stderrInoutStream = process.getErrorStream();OutputStream stderrOutputSteam = new FileOutputStream(stderrFile);InputStream stdoutInputStream = process.getInputStream();OutputStream stdOutputStream = new FileOutputStream(stdoutFile)){// 获取标准错误输入流int ch;// 将错误信息读入到错误日志文件while ((ch = stderrInoutStream.read()) != -1) {stderrOutputSteam.write(ch);}// 将子进程标准输出写入到指定文件while ((ch = stdoutInputStream.read()) != -1) {stdOutputStream.write(ch);}}}// 进程等待exitCode = process.waitFor();} catch (IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}return exitCode;
}

拼接编译命令时通过 -d指定编译后的文件存放到指定位置,不然找不到字节码文件位置。

// 2.拼接编译命令
String compileCmd = String.format("javac -encoding utf8 %s -d %s",classFile,workDir);
//4.运行代码
String runCmd = String.format("java -classpath %s %s",workDir,className);

4.统一功能处理

统一登录拦截

定义拦截器:

  1. 创建自定义拦截器,实现Handlerlnterceptor接口的preHandle(执行具体方法之前的预处理)方法
  2. 将自定义拦截器加入WebMvcConfigureraddInterceptors

提供一个管理员页面来对题目进行添加和修改。管理员页面使用拦截器对普通用户进行拦截.

@Configuration
public class AppConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/login").excludePathPatterns("/user/reg").excludePathPatterns("/user/verificationCode").excludePathPatterns("/login.html").excludePathPatterns("/reg.html").excludePathPatterns("/css/**").excludePathPatterns("/js/**").excludePathPatterns("/img/**");registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/admin.html").addPathPatterns("/addProblem.html").addPathPatterns("/problem/update").addPathPatterns("/problem/add");}
}

统一格式返回

统一的数据返回格式使用@ControllerAdvice+ResponseBodyAdvice实现

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {@Resourceprivate ObjectMapper objectMapper;@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body instanceof Response) {return body;}if (body instanceof  String) {try {return objectMapper.writeValueAsString(body);} catch (JsonProcessingException e) {e.printStackTrace();}}return Response.success(body);}
}

统一异常处理

@ControllerAdvice
public class ExceptionAdvice {@ExceptionHandler(Exception.class)@ResponseBodypublic Response exceptionAdvice(Exception e) {return Response.fail("服务器异常");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397955.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

有序转化数组(LeetCode)

题目 给你一个已经 排好序 的整数数组 和整数 、 、 。对于数组中的每一个元素 ,计算函数值 ,请 按升序返回数组 。 解题 在时间复杂度为解决问题 def sortTransformedArray(nums, a, b, c):def f(x):return a * x * x b * x cn len(nums)result…

4个从阿里毕业的P7打工人,当起了包子铺的老板

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&chksmc0e47813f793f105017fb8551c9b996dc7782987e19efb166ab665f44ca6d900210e6c4c0281&scene21#wechat_redirect 《网安面试指南》h…

学生公寓电费信息管理小程序的设计

管理员账户功能包括:系统首页,个人中心,公寓管理员管理,学生管理,楼层信息管理,用电情况管理,缴费清单管理,系统管理 微信端账号功能包括:系统首页,用电情况…

【数据结构】六、图:4.图的遍历(深度优先算法DFS、广度优先算法BFS)

三、基本操作 文章目录 三、基本操作1.图的遍历1.1 深度优先遍历DFS1.1.1 DFS算法1.1.2 DFS算法的性能分析1.1.3 深度优先的生成树和生成森林 1.2 广度优先遍历BFS1.2.1 BFS算法1.2.2 BFS算法性能分析1.2.3 广度优先的生成树和生成森林 1.3 图的遍历与图的连通性 1.图的遍历 图…

Nginx系列-Nginx Location匹配规则

文章目录 Nginx系列-Nginx Location匹配规则1. 语法基础2. 匹配规则2.1 精确匹配()2.2. 最长前缀匹配(^~)2.3. 正则表达式匹配(~和~*)2.4. 普通前缀匹配(无修饰符)2.5. 默认匹配&…

贷齐乐hpp+php特性注入

文章目录 运行过程waf第一层waf拦截第二层waf拦截 数据库查询语句注入思路注入 运行过程 foreach ($_REQUEST as $key > $value) {$_REQUEST[$key] dowith_sql($value);}$request_uri explode("?", $_SERVER[REQUEST_URI]);if (isset($request_uri[1])) {$rewr…

OpenGL3.3_C++_Windows(34)

demo 1 Fresnel-Schlick PBR直接光源 顾名思义:直接光源指有光源直接照射到点p 的辐射强度,由于一个光源只会有一个光线wi影响点p,所以和之前的计算没什么差异对于影响p的光源,并不需要积分计算半球形辐照度,遍历每个…

redis面试(十)锁释放

自动释放 首先锁的释放分为两种,一种是自动释放,加入说加锁的线程宕机了不在了,我们之前说过这个。 那这个线程中的对redis这个锁不断刷新过期时间的看门狗逻辑就没有了,所以这个锁最多等待30s的时间就会自动过期删除&#xff0c…

为什么选择在Facebook投放广告?

2024年了你还没对 Facebook 广告产生兴趣?那你可就亏大了! 今天这篇文章,我们会分享它对你扩大业务的好处。要知道,Facebook 广告凭借它庞大的用户群和先进的定位选项,已经是企业主们有效接触目标受众的必备神器。接下…

【uniapp】uniapp+vue2微信小程序实现分享功能

uniappvue2做的微信小程序实现分享功能 问题描述 uniappvue2做的微信小程序,发布以后点击右上角三个点,分享小程序的时候,转发和分享按钮都是灰色 解决方案 转发、分享、复制链接这几个功能需要自己来手动写方法,考虑到每个页…

Unity入门3——脚本入门

本文使用的代码编辑器为VSCode 安装接口有: 通过将变量设置为public,可以直接在unity的Inspector面板中看到相关变量。此时可直接将需要的素材拖拽到变量处。 [SerializeField]可序列化:定义后可以使非公共的属性也显示在unity面板 [Range]…

搜维尔科技:【研究】大屏幕沉浸式系统的优势,视觉冲击强、‌分辨率高、‌画面层次感强以及沉浸式交互性体验好等!

大屏幕沉浸式系统的优势主要体现在视觉冲击强、‌分辨率高、‌画面层次感强以及沉浸式交互性体验好。‌ 视觉冲击强:‌大屏幕沉浸式系统通过使用多台投影机投射画面,‌结合高质量影片,‌营造出场景环境,‌通过视觉艺术直击体验者…

js 深入理解原型(prototype)及如何创建对象

目录 1. 概述2. 工厂模式3. 构造函数模式3.1 创建的格式3.2 JS内部执行步骤3.3 constructor 构造器3.4 构造函数也是函数3.5 构造函数的问题 4. 原型模式 prototype4.1 理解原型本质4.2 原型层级(访问一个属性,查询的次序)4.2.1 查询次序:实例…

SeaTunnel 实战: Apache SeaTunnel 安装与部署

文章目录 一、准备工作1.1 环境1.2 下载 二、SeaTunnel安装2.1 解压安装包2.2.配置环境变量2.3.配置立刻生效2.4 下载SeaTunnel相关jar包2.5 测试验证2.6 启动服务 三、SeaTunnel Web 1.0.1安装3.1 将下载的压缩包解压缩到指定目录下3.2 设置 SeaTunnel Web 环境变量3.3 初始化…

pythonUI自动化008::allure测试报告(安装及应用)

allure报告预览 1 下载jdk,配置jdk Path变量: https://www.cnblogs.com/FBGG/p/15103119.html(这里不作阐述,请看该偏文章配置即可) 2 下载allure驱动,配置allure Path变量: 下载allure驱动&a…

【免费】最新区块链钱包和私钥的助记词碰撞器,bybit使用python开发

使用要求 1、用的是google里面的扩展打包成crx文件,所以在使用之前你需要确保自己电脑上有google浏览器,而且google浏览器版本需要在124之上。(要注意一下,就是电脑只能有一个Chrome浏览器) 2、在win10上用vscode开发…

锂电池剩余寿命预测 | Matlab基于Transformer-GRU的锂电池剩余寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于Transformer-GRU的锂电池剩余寿命预测,Transformer结合门控循环单元。 Matlab基于Transformer-GRU的锂电池剩余寿命预测(单变量) 运行环境Matlab2023b及以上。 首先从…

C++初阶:模版进阶【非类型模版参数】【模版的特化】【模版分离编译】

目录 一.非类型模版参数 二.模版的特化 2.1模版特化的概念 2.2函数模版的特化 2.3类模版特化 2.3.1全特化 2.3.2偏特化 2.3.3使用类模版特化 三.模版分离编译 一.非类型模版参数 模板参数分类类型形参与非类型形参。 类型形参:出现在模板参数列表中&…

【数据结构算法经典题目刨析(c语言)】使用队列实现栈(图文详解)

目录 一.题目描述 二.解题思路 三.代码实现 💓 博客主页:C-SDN花园GGbond ⏩ 文章专栏:数据结构经典题目刨析(c语言) 一.题目描述 二.解题思路 首先这道题需要我们使用两个队列来完成栈的实现, 这里我们的思路是, 栈的要求是后进先出, …

C语言进阶——一文带你深度了解“C语言关键字”(中篇6)

本篇文章记录我学习C语言进阶知识——C语言关键字,旨在记录分享,希望我的分享能带给你不一样的收获! 目录 一、return关键字 二、const 关键字也许该被替换为 readolny (一)、 const 修饰的只读变量 (二…