微服务项目->在线oj系统(Java版 - 5)

相信自己,终会成功

微服务代码:  lyyy-oj: 微服务

目录

C端代码

用户题目接口

 修改后用户提交代码(应用版)

 用户提交题目判题结果

代码沙箱

1. 代码沙箱的核心功能

2. 常见的代码沙箱实现方式

3. 代码沙箱的关键问题与解决方案

4. 你的代码如何与沙箱交互?

6. 总结

Elasticsearch

RabbitMQ

RabbitMQ 主要功能

RabbitMQ 工作流程示例

典型应用场景


C端代码

用户题目接口

从前端接收到数据,判断是什么语言,如果是Java语言(目前只能进行Java语言的判定)

利用代码沙箱(下方有介绍),对语言进行判断

assembleJudgeSubmitDTO:拿到questionId , 从es中查询题目信息

如果questionES不等于空,

BeanUtil.copyProperties(questionES,judgeSubmitDTO),将questionES复制给 judgeSubmitDTO

否则,从数据库中查找数据,将查出的数据复制给judgeSubmitDTO

然后将数据存入es中

将从线程池中拿到的数据赋值给judgeSubmitDTO

拼接代码后进行解析测试用例

步骤说明
questionCaseList.stream()List<QuestionCase>转为Stream<QuestionCase>,支持链式操作
.map(QuestionCase::getInput)提取每个QuestionCase对象的input字段(方法引用,等价于x -> x.getInput()
.toList()Stream<String>收集为不可变List<String>(Java 16+特性)
setInputList/setOutputList最终将输入/输出列表设置到判题DTO对象

数据流转示例

假设原始数据:

List<QuestionCase> questionCaseList = [{"input": "1 2", "output": "3"},{"input": "3 4", "output": "7"}
]

 转换后结果:

inputList = ["1 2", "3 4"]  // 所有input的集合
outputList = ["3", "7"]      // 所有output的集合
 @Override//后端接收到请求,获取参数,根据getProgramType判断用户提交代码语言类型//    UserSubmitDTO(用户提交的数据,包括题目ID、代码、考试ID等)
//    JudgeSubmitDTO(判题服务需要的数据,包括题目信息、测试用例、用户代码等)public R<UserQuestionResultVO> submit(UserSubmitDTO submitDTO) {Integer programType = submitDTO.getProgramType();if(ProgramType.JAVA.getValue().equals(programType)){//按照Java逻辑处理JudgeSubmitDTO judgeSubmitDTO=assembleJudgeSubmitDTO(submitDTO);
//            remoteJudgeService.doJudgeJavaCode(judgeSubmitDTO)return remoteJudgeService.doJudgeJavaCode(judgeSubmitDTO);}throw new ServiceException(ResultCode.FAILED_NOT_SUPPORT_PROGRAM);}private JudgeSubmitDTO assembleJudgeSubmitDTO(UserSubmitDTO submitDTO) {Long questionId = submitDTO.getQuestionId();//orElse,findbyId返回的是Optional对象,并不是数据本身,如果能查出数据,返回数据本身//查不出来数据,返回null// 1. 查询题目信息(优先ES,不存在则查MySQL并缓存)QuestionES questionES = questionRepository.findById(questionId).orElse(null);JudgeSubmitDTO judgeSubmitDTO=new JudgeSubmitDTO();if(questionES!=null){BeanUtil.copyProperties(questionES,judgeSubmitDTO);}else{Question question = questionMapper.selectById(questionId);BeanUtil.copyProperties(question,judgeSubmitDTO);questionES=new QuestionES();// 2. 组装 JudgeSubmitDTOBeanUtil.copyProperties(question, questionES);questionRepository.save(questionES);}// 3. 设置用户信息(从 ThreadLocal 获取用户ID)judgeSubmitDTO.setUserId(ThreadLocalUtil.get(Constants.USER_ID,Long.class));judgeSubmitDTO.setExamId(submitDTO.getExamId());judgeSubmitDTO.setProgramType(submitDTO.getProgramType());// 4.拼接用户代码和题目主函数judgeSubmitDTO.setUserCode(codeConnect(submitDTO.getUserCode(),questionES.getMainFuc()));// 5. 解析测试用例(从 ES 的 JSON 字符串转换成 List)List<QuestionCase> questionCaseList = JSONUtil.toList(questionES.getQuestionCase(), QuestionCase.class);List<String> inputList = questionCaseList.stream().map(QuestionCase::getInput).toList();judgeSubmitDTO.setInputList(inputList);List<String> outputList = questionCaseList.stream().map(QuestionCase::getOutput).toList();judgeSubmitDTO.setOutputList(outputList);return judgeSubmitDTO;}

 修改后用户提交代码(应用版)

JudgeProducer(使用了RabbitMQ(下方有介绍))

 /*** 将用户提交的代码通过RabbitMQ异步发送给判题服务(目前仅支持Java)* @param submitDTO 用户提交的代码信息,包含代码内容、题目ID、编程语言类型等* @return true 提交成功 | 抛出异常 提交失败(不支持的编程语言)* @throws ServiceException 如果编程语言不支持,抛出业务异常(ResultCode.FAILED_NOT_SUPPORT_PROGRAM)*/@Overridepublic boolean rabbitSubmit(UserSubmitDTO submitDTO) {// 1. 获取用户提交的编程语言类型Integer programType = submitDTO.getProgramType();if(ProgramType.JAVA.getValue().equals(programType)){//按照Java逻辑处理, 组装判题服务需要的DTO(包括代码、测试用例等信息)JudgeSubmitDTO judgeSubmitDTO=assembleJudgeSubmitDTO(submitDTO);// 通过RabbitMQ生产者将判题任务发送到消息队列(异步处理)
//             把参数给rabbitmq,但是没执行判题结果,目前实现是同步调用远程服务judgeProducer.produceMsg(judgeSubmitDTO);// 返回true表示消息已成功提交到队列(注意:不表示判题已完成)return true;}throw new ServiceException(ResultCode.FAILED_NOT_SUPPORT_PROGRAM);}
@Component
//@Component:将该类标记为 Spring 组件,由 Spring 容器管理
@Slf4j
public class JudgeProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void produceMsg(JudgeSubmitDTO judgeSubmitDTO) {try {
//            使用 RabbitTemplate 向 RabbitMQ
//            发送消息消息内容是 JudgeSubmitDTO(判题提交数据传输对象)
//            发送到名为 OJ_WORK_QUEUE 的队列rabbitTemplate.convertAndSend(RabbitMQConstants.OJ_WORK_QUEUE, judgeSubmitDTO);} catch (Exception e) {log.error("生产者发送消息异常", e);throw new ServiceException(ResultCode.FAILED_RABBIT_PRODUCE);}}
}

 用户提交题目判题结果

  /*** 根据考试ID、题目ID和时间戳查询用户的判题结果* @param examId 考试ID* @param questionId 题目ID* @param currentTime 提交时间标识(用于区分同一题目的多次提交)* @return UserQuestionResultVO 包含判题状态、执行结果、用例详情等*/@Overridepublic UserQuestionResultVO exeResult(Long examId, Long questionId, String currentTime) {//把结果获取出来// 1. 从ThreadLocal中获取当前用户ID(基于登录上下文)Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);// 2. 查询数据库获取用户提交记录UserSubmit userSubmit = userSubmitMapper.selectCurrentUserSubmit(userId, questionId, examId, currentTime);// 3. 构建返回VO对象UserQuestionResultVO resultVO = new UserQuestionResultVO();// 4. 判题结果不存在的情况(可能还在判题中)if (userSubmit == null) {resultVO.setPass(QuestionResType.IN_JUDGE.getValue()); // 设置状态为"判题中"}// 5. 存在判题结果else {resultVO.setPass(userSubmit.getPass());resultVO.setExeMessage(userSubmit.getExeMessage());if (StrUtil.isNotEmpty(userSubmit.getCaseJudgeRes())) {resultVO.setUserExeResultList(JSON.parseArray(userSubmit.getCaseJudgeRes(), UserExeResult.class));}}return resultVO;}

代码沙箱

代码沙箱(Code Sandbox)是一种安全隔离的执行环境,用于运行不受信任的代码(如用户提交的编程题答案),防止恶意代码影响主系统。在在线判题系统(Online Judge)中,代码沙箱是核心组件之一。

1. 代码沙箱的核心功能

功能

说明

安全隔离

防止用户代码破坏主机(如删除文件、无限循环、占用资源)。

资源限制

限制 CPU、内存、执行时间,避免恶意代码耗尽系统资源。

输入/输出控制

提供标准输入(测试用例),捕获标准输出/错误,与判题系统交互。

多语言支持

支持 Java、Python、C++ 等语言的编译和运行。

错误处理

捕获运行时异常、编译错误,并返回友好提示。

2. 常见的代码沙箱实现方式

1.基于 Docker 的沙箱

原理:每个用户提交的代码在一个临时 Docker 容器中运行,运行后销毁。

优点

强隔离性(进程、文件系统、网络均隔离)。

可限制 CPU、内存等资源(通过 cgroups)。

示例流程

用户提交代码 → 判题系统接收。

生成临时 Docker 容器,挂载代码文件。

在容器内编译/运行代码,传入测试用例。

捕获输出,对比预期结果。

销毁容器。

2. 基于 JVM 沙箱(Java 专用)

原理:利用 Java 的 SecurityManager 或字节码修改(如 ASM)限制敏感操作。

优点

轻量级,启动快。

适合纯 Java 判题场景。

缺点

无法完全隔离系统调用(如 System.exit())。

需要自定义安全策略。

3. 第三方沙箱服务

示例

Judge0:开源的在线判题沙箱(支持 60+ 语言)。

Piston:轻量级多语言执行引擎。

优点:无需自行维护沙箱环境。

3. 代码沙箱的关键问题与解决方案

问题

解决方案

恶意代码

使用 Docker 隔离,限制系统调用(如 forkexec)。

无限循环

设置超时机制(如 Linux 的 timeout 命令)。

内存溢出

通过 -Xmx 限制 JVM 内存,或 Docker --memory 限制容器内存。

文件系统安全

Docker 使用只读文件系统,或临时挂载空目录。

网络隔离

禁用容器网络(--network none)。

4. 你的代码如何与沙箱交互?

执行流程

用户提交代码 → 你的服务组装 JudgeSubmitDTO(题目ID、代码、测试用例等)。

通过 Feign 调用判题服务(remoteJudgeService.doJudgeJavaCode)。

判题服务将代码发送到 代码沙箱 执行。

沙箱返回结果(通过/失败、错误信息、用时等)。

你的服务接收结果并返回给用户。

6. 总结

代码沙箱 是判题系统的核心,确保安全性和稳定性。

推荐方案

小型系统:用 Docker 快速实现。

大型系统:结合 Kubernetes 管理沙箱集群。

扩展方向

支持更多语言(Python、C++)。

分布式判题(提高并发能力)。

为什么需要沙箱?

安全隔离:防止用户代码破坏宿主系统。

资源控制:限制CPU/内存使用,避免恶意代码耗尽资源。

环境一致性:确保每次执行都在干净的环境中运行。


Elasticsearch

官方网站:

Elastic Docs | Elastic

Elasticsearch(简称 ES)是一个开源的分布式 搜索和分析引擎,基于 Apache Lucene 构建,专为处理海量数据设计,支持近实时(NRT, Near Real-Time)搜索。

优点

高性能搜索,支持复杂查询(全文检索、模糊匹配、聚合分析)。

水平扩展能力强,适合大数据场景。

生态完善(ELK Stack、APM、SIEM 等)。

缺点

不支持事务(不适合金融级一致性要求场景)。

资源消耗较高(尤其是内存)。

学习曲线较陡(需理解分词、映射、集群管理等)


RabbitMQ

RabbitMQ 是一个开源的 消息代理(Message Broker),实现了 AMQP(Advanced Message Queuing Protocol) 协议,用于在分布式系统中存储、转发消息。

核心角色:生产者(Producer)→ RabbitMQ → 消费者(Consumer)

典型场景:异步任务处理、应用解耦、流量削峰、分布式系统通信。

概念说明
Producer消息生产者,发送消息到 Exchange
Consumer消息消费者,从 Queue 接收消息
Exchange消息路由组件,决定消息投递到哪些 Queue(类型:Direct、Fanout、Topic、Headers)
Queue存储消息的缓冲区,消费者从中订阅消息
BindingExchange 和 Queue 的绑定规则(如路由键 Routing Key)
Channel轻量级连接(复用 TCP 连接,减少开销)
Virtual Host虚拟隔离环境(类似命名空间,不同 vhost 资源互不干扰)

RabbitMQ 主要功能

消息路由(Exchange Types)

Direct Exchange
→ 精确匹配 Routing Key,消息投递到完全匹配的 Queue。

// 示例:日志级别路由(error、warning、info)
channel.queueBind("error_queue", "logs_exchange", "error");

Fanout Exchange
→ 广播模式,消息发送到所有绑定的 Queue(忽略 Routing Key)。

// 示例:新闻通知广播
channel.exchangeDeclare("news", BuiltinExchangeType.FANOUT);

 Topic Exchange
→ 通配符匹配 Routing Key* 匹配一个词,# 匹配多个词)。

// 示例:订单路由(order.create、order.payment.success)
channel.queueBind("queue_payment", "orders", "order.payment.*");

 Headers Exchange
→ 基于消息头(Headers)匹配,不依赖 Routing Key(性能较低,较少使用)。

 消息可靠性

消息确认(ACK/NACK)
→ 消费者处理成功后发送 ACK,失败时 NACK(可配置重试或进入死信队列)。

channel.basicConsume(queue, false, consumer); // 手动ACK
channel.basicAck(deliveryTag, false); // 确认处理成功

 持久化(Persistence)
→ Exchange、Queue、消息均可持久化到磁盘,防止服务重启丢失。

// 声明持久化队列
channel.queueDeclare("task_queue", true, false, false, null);

 死信队列(DLX)
→ 处理失败或超时的消息可转发到死信队列,用于异常监控和重试。

Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx_exchange");
channel.queueDeclare("normal_queue", false, false, false, args);

高级特性

TTL(Time-To-Live)
→ 设置消息或队列的过期时间(超时未消费则自动删除)。

// 消息级别TTL
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder().expiration("60000") // 60秒过期.build();
channel.basicPublish(exchange, routingKey, props, message.getBytes());

优先级队列(Priority Queue)
→ 消息按优先级消费(需队列声明时支持)。

Map<String, Object> args = new HashMap<>();
args.put("x-max-priority", 10); // 最大优先级为10
channel.queueDeclare("priority_queue", false, false, false, args);

 集群与镜像队列
→ 支持多节点集群,镜像队列(Mirrored Queue)实现高可用。

RabbitMQ 工作流程示例

典型应用场景

  1. 异步任务处理
    → 用户注册后异步发送邮件/短信。

  2. 应用解耦
    → 订单系统与库存系统通过消息队列通信。

  3. 流量削峰
    → 秒杀请求先写入队列,后端按能力消费。

  4. 日志收集
    → 多个服务发送日志到统一队列,由消费者存储到ES/数据库。

 对比其他消息队列

RabbitMQKafkaRocketMQ
协议AMQP自定义协议自定义协议
吞吐量中等(万级TPS)高(百万级TPS)高(十万级TPS)
延迟低(毫秒级)中(依赖批量)
适用场景业务消息、实时处理日志流、大数据金融级事务消息

RabbitMQ 是轻量级、高可用的消息中间件,适合需要可靠消息传递的分布式系统。通过灵活的路由规则和丰富的特性,平衡了性能与功能需求 

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

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

相关文章

Vue3 Element Plus 中el-table-column索引使用问题

在 Element Plus 的 el-table 组件中&#xff0c;使用 scope.index 是不准确的。正确的索引属性应该是 scope.$index。你的代码需要调整为&#xff1a; vue 复制 下载 <el-button type"primary" size"default" text click"onModifyClick(scope…

Ubuntu20.04下使用dpkg方式安装WPS后,将WPS改为中文界面方法

Ubuntu20.04下使用dpkg方式安装WPS后&#xff0c;将WPS改为中文界面方法 说明方法 说明 Ubuntu20.04下使用dpkg方式安装WPS后&#xff0c;打开WPS后&#xff0c;发现界面是英文的&#xff0c;如有需要可以按照下面的方法将其改为中文界面。 方法 cd /opt/kingsoft/wps-offic…

【​​HTTPS基础概念与原理​】​​HTTPS vs HTTP:为什么现代网站必须用HTTPS?

以下是关于 HTTPS vs HTTP 的详细对比分析&#xff0c;涵盖安全性、性能差异及SEO影响&#xff0c;帮助您全面理解为何现代网站必须采用HTTPS&#xff1a; 一、安全性对比&#xff1a;HTTPS 如何解决 HTTP 的致命缺陷 1. HTTP 的安全隐患 • 明文传输&#xff1a;HTTP 数据以明…

算法刷题(Java与Python)1.二分查找

目录 二分查找 思路 总体 细节 问题一&#xff0c;为什么循环的条件是left<right ,为什么要有等号呢 问题二&#xff0c;为什么中间值是left (right - left) / 2 问题三&#xff0c;为什么最后返回的是左边的值呢 情况 1&#xff1a;target 存在于数组中 情况 2&a…

芯片生态链深度解析(二):基础设备篇——人类精密制造的“巅峰对决”

【开篇&#xff1a;设备——芯片工业的“剑与盾”】 当ASML的EUV光刻机以每秒5万次激光脉冲在硅片上雕刻出0.13nm精度的电路&#xff08;相当于在月球表面精准定位一枚二维码&#xff09;&#xff0c;当国产28nm光刻机在华虹产线实现“从0到1”的突破&#xff0c;这场精密制造…

MongoTemplate 基础使用帮助手册

前言 MongoDB 是一种流行的 NoSQL 数据库&#xff0c;适合存储大量的非结构化数据。MongoTemplate 是 Spring Data MongoDB 中的一个核心组件&#xff0c;它提供了一组丰富的 API 来与 MongoDB 进行交互。它封装了许多常见的数据库操作&#xff0c;使开发者能够轻松执行 CRUD 操…

psotgresql18 源码编译安装

环境&#xff1a; 系统&#xff1a;centos7.9 数据库&#xff1a;postgresql18beta1 #PostgreSQL 18 已转向 DocBook XML 构建体系&#xff08;SGML 未来将被弃用&#xff09;。需要安装 XML 工具链&#xff0c;如下&#xff1a; yum install -y docbook5-style-xsl libxsl…

C++编程起步项目

员工信息管理系统 需求 Employee.h #pragma once#include<iostream> #include<string>using namespace std;class Employee { public:int id; // 编号string name; // 姓名string position; // 岗位int deptId; // 部门编号Employee();Employee(int id, string n…

Linux的MySQL头文件和找不到头文件问题解决

头文件 #include <iostream> #include <mysql_driver.h> #include <mysql_connection.h> #include <cppconn/statement.h> #include <cppconn/resultset.h> #include <cppconn/prepared_statement.h> #include <cppconn/exception.h&g…

[ linux-系统 ] 命令行参数 | 环境变量

命令行参数 命令行参数是指用户在启动程序时通过命令行传递给程序的参数。这些参数可以用于控制程序的行为、传递输入数据或配置选项。 在 C/C 中&#xff0c;命令行参数通过 main 函数的参数传递 命令行参数列表 argc:参数的个数 argv[]&#xff1a;参数的清单 为什么要…

新书速览|鸿蒙HarmonyOS NEXT开发之路 卷2:从入门到应用篇

《鸿蒙HarmonyOS NEXT开发之路 卷2&#xff1a;从入门到应用篇》 01 本书内容 《鸿蒙HarmonyOS NEXT开发之路 卷2&#xff1a;从入门到应用篇》是一本深度聚焦HarmonyOS NEXT应用开发的全方位指导书&#xff0c;内容遵循由浅入深的原则展开。全书分为基础知识、应用开发进阶和…

经典密码学和现代密码学的结构及其主要区别(1)凯撒密码——附py代码

密码学是一门通过使用代码和密码来保护信息的艺术与科学&#xff0c;其历史可以追溯到数千年前。古典密码学代表了这一古老学科早期的篇章。早在计算机和现代加密算法出现之前&#xff0c;历史上的各个文明就依靠巧妙的方法来保护机密、安全通信以及获取战略优势。 古典密码学…

Python60日基础学习打卡D30

回顾&#xff1a; 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; # 直接导入 from random import randint print(randint(1, 10)) # 导入自定义库 import module m…

Linux利用多线程和线程同步实现一个简单的聊天服务器

1. 概述 本文实现一个基于TCP/IP的简单多人聊天室程序。它包含一个服务器端和一个客户端&#xff1a;服务器能够接收多个客户端的连接&#xff0c;并将任何一个客户端发来的消息广播给所有其他连接的客户端&#xff1b;客户端则可以连接到服务器&#xff0c;发送消息并接收来自…

ubuntu系统 | dify+ollama+deepseek搭建本地应用

1、安装 Ollama 下载并安装 Ollama (llm) wangqiangwangqiang:~$ curl -fsSL https://ollama.ai/install.sh | bash >>> Installing ollama to /usr/local >>> Downloading Linux amd64 bundle0.3% curl -fsSL https://ollama.ai/install.sh &#xff08;下…

从纸质契约到智能契约:AI如何改写信任规则与商业效率?​——从智能合约到监管科技,一场颠覆传统商业逻辑的技术革命

一、传统合同的“低效困境”&#xff1a;耗时、昂贵、风险失控 近年来&#xff0c;全球商业环境加速向数字化转型&#xff0c;但合同管理却成为企业效率的“阿喀琉斯之踵”。据国际商会&#xff08;International Chamber of Commerce&#xff09;数据显示&#xff0c;全球企业…

【机器学习|学习笔记】基于生成对抗网络的孪生框架(GAN-based Siamese framework,GSF)详解,附代码。

【机器学习|学习笔记】基于生成对抗网络的孪生框架(GAN-based Siamese framework,GSF)详解,附代码。 【机器学习|学习笔记】基于生成对抗网络的孪生框架(GAN-based Siamese framework,GSF)详解,附代码。 文章目录 【机器学习|学习笔记】基于生成对抗网络的孪生框架(G…

UEFI Spec 学习笔记---33 - Human Interface Infrastructure Overview---33.2.6 Strings

33.2.6 Strings UEFI 环境中的 string 是使用 UCS-2 格式定义&#xff0c;每个字符由 16bit 数据表示。对于用户界面&#xff0c;strings 也是一种可以安装到 HIIdatabase 的一种数据。 为了本土化&#xff0c;每个 string 通过一个唯一标识符来识别&#xff0c;而每一个标识…

Stable Diffusion 学习笔记02

模型下载网站&#xff1a; 1&#xff0c;LiblibAI-哩布哩布AI - 中国领先的AI创作平台 2&#xff0c;Civitai: The Home of Open-Source Generative AI 模型的安装&#xff1a; 将下载的sd模型放置在sd1.5的文件内即可&#xff0c;重启客户端可用。 外挂VAE模型&#xff1a…

并发编程(5)

抛异常时会释放锁。 当线程在 synchronized 块内部抛出异常时&#xff0c;会自动释放对象锁。 public class ExceptionUnlockDemo {private static final Object lock new Object();public static void main(String[] args) {Thread t1 new Thread(() -> {synchronized …