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

news2025/5/20 19:54:46

相信自己,终会成功

微服务代码:  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. 组装 JudgeSubmitDTO
            BeanUtil.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)
     */
    @Override
    public 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 {

    @Autowired
    private 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 包含判题状态、执行结果、用例详情等
     */
    @Override
    public 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.coloradmin.cn/o/2380210.html

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

相关文章

get请求使用数组进行传参

get请求使用数组进行传参,无需添加中括号 mvc接口要添加参数名&#xff0c;使用array承接。不能用list, 否则会报错 这里是用apifox模拟前端调用。 前端调用代码 // 根据项目ID和角色ID查询相关审批人 export function findRelativeApproverByProjectIdAndRoleId(roleIds, p…

【MySQL成神之路】MySQL常用语法总结

目录 MySQL 语法总结 数据库操作 表操作 数据操作 查询语句 索引操作 约束 事务控制 视图操作 存储过程和函数 触发器 用户和权限管理 数据库操作 创建数据库&#xff1a; CREATE DATABASE database_name; 选择数据库&#xff1a; USE database_name; 删除数…

Linux动静态库制作与原理

什么是库 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。现实中每个程序都要依赖很多基础的底层库&#xff0c;不可能每个人的代码都从零开始&#xff0c;因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统…

ffmpeg 把一个视频复制3次

1. 起因&#xff0c; 目的: 前面我写过&#xff0c;使用 python 把一个视频复制3次但是速度太慢了&#xff0c;我想试试看能否改进。而且我想换一种新的视频处理思路&#xff0c;并试试看速度如何。 2. 先看效果 效果就是能行&#xff0c;而且速度也快。 3. 过程: 代码 1…

GPT/Claude3国内免费镜像站更新 亲测可用

无限次使用&#xff1a;无限制的提问次数&#xff0c;不设上限&#xff0c;随心所欲。 无需魔法、稳定流畅&#xff1a;操作简便&#xff0c;无需复杂设置&#xff0c;即可享受稳定流畅的服务。 手机和电脑均能用&#xff1a;轻松适配手机和电脑&#xff0c;使用体验更佳。 …

Python:操作Excel按行写入

Python按行写入Excel数据,5种实用方法大揭秘! 在日常的数据处理和分析工作中,我们经常需要将数据写入到Excel文件中。Python作为一门强大的编程语言,提供了多种库和方法来实现将数据按行写入Excel文件的功能。本文将详细介绍5种常见的Python按行写入Excel数据的方法,并附上…

Redis进阶知识

Redis 1.事务2. 主从复制2.1 如何启动多个Redis服务器2.2 监控主从节点的状态2.3 断开主从复制关系2.4 额外注意2.5拓扑结构2.6 复制过程2.6.1 数据同步 3.哨兵选举原理注意事项 4.集群4.1 数据分片算法4.2 故障检测 5. 缓存5.1 缓存问题 6. 分布式锁 1.事务 Redis的事务只能保…

12.vue整合springboot首页显示数据库表-实现按钮:【添加修改删除查询】

vue整合springboot首页显示数据库表&#xff1a;【添加修改删除查询】 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是node.js和vue的使用。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性。【帮帮志系…

bisheng系列(一)- 本地部署(Docker)

目录 一、导读 二、说明 1、镜像说明 2、本节内容 三、docker部署 1、克隆代码 2、运行镜像 3、可能的错误信息 四、页面测试 1、注册用户 2、登陆成功 3、添加模型 一、导读 环境&#xff1a;Ubuntu 24.04、Windows 11、WSL 2、Python 3.10 、bisheng 1.1.1 背景…

如何用Python批量解压ZIP文件?快速解决方案

如何用Python批量解压ZIP文件&#xff1f;快速解决方案 文章目录 **如何用Python批量解压ZIP文件&#xff1f;快速解决方案**代码结果详细解释 话不多说&#xff0c;先上干货&#xff01;&#xff01;&#xff01; 代码 import os import zipfiledef unzip_file(dir_path: str…

DriveGenVLM:基于视觉-语言模型的自动驾驶真实世界视频生成

《DriveGenVLM: Real-world Video Generation for Vision Language Model based Autonomous Driving》2024年8月发表&#xff0c;来自哥伦比亚大学的论文。 自动驾驶技术的进步需要越来越复杂的方法来理解和预测现实世界的场景。视觉语言模型&#xff08;VLM&#xff09;正在成…

企业标准信息公共服务平台已开放标准通编辑器访问入口

标准通 数字化标准编辑器 专业、高效、便捷 企业标准信息公共服务平台 近日&#xff0c;企业标准信息公共服务平台已开放标准通编辑器访问入口&#xff0c;可进入官网指定版块使用&#xff01; 核心功能亮点 解决企业痛点 传统标准编制&#xff0c;需反复核对格式、逐条…

进阶-数据结构部分:1、数据结构入门

飞书文档https://x509p6c8to.feishu.cn/wiki/HRLkwznHiiOgZqkqhLrcZNqVnLd 一、存储结构 顺序存储 链式存储 二、常用数据结构 2.1、栈 先进后出 场景&#xff1a; 后退/前进功能&#xff1a;网页浏览器中的后退和前进按钮可以使用栈来实现。在浏览网页时&#xff0c;每次…

React 19中useContext不需要Provider了。

文章目录 前言一、React 19中useContext移除了Provider&#xff1f;二、使用步骤总结 前言 在 React 19 中&#xff0c;useContext 的使用方式有所更新。开发者现在可以直接使用 作为提供者&#xff0c;而不再需要使用 <Context.Provider>。这一变化简化了代码结构&…

Json schema校验json字符串(networknt/json-schema-validator库)

学习链接 json-schema官网 - 英文 jsonschemavalidator 可在线校验网站 networknt的json-schema-validator github地址 networknt的json-schema-validator 个人gitee地址 - 里面有md文档说明和代码示例 JSON Schema 入门指南&#xff1a;如何定义和验证 JSON 数据结构 JS…

交易所开发:构建功能完备的金融基础设施全流程指南

交易所开发&#xff1a;构建功能完备的金融基础设施全流程指南 ——从技术架构到合规安全的系统性解决方案 一、开发流程&#xff1a;从需求分析到运维优化 开发一款功能完备的交易所需要遵循全生命周期管理理念&#xff0c;涵盖市场定位、技术实现、安全防护和持续迭代四大阶…

Axure疑难杂症:统计分析页面引入Echarts示例动态效果

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! Axure产品经理精品视频课已登录CSDN可点击学习https://edu.csdn.net/course/detail/40420 课程主题:统计分析页面引入Echarts示例动态效果 主要内容:echart示例引入、大小调整、数据导入 应用场景:统计分析页面…

展锐Android14及更新版本split_build编译方法

更改split_build.py文件内容后按照下面方法编译&#xff1a; zip -r sys/vendor/sprd/release/split_build.zip sys/vendor/sprd/release/split_build/ rm -r sys/vendor/sprd/release/split_build/ cp -r vnd/vendor/sprd/release/split_build/ sys/vendor/sprd/release/cd s…

青少年ctf平台应急响应-应急响应2

题目&#xff1a; 当前服务器被创建了一个新的用户&#xff0c;请提交新用户的用户名&#xff0c;得到的结果 ssh rootchallenge.qsnctf.com -p 30327 这个命令用于通过 SSH 协议连接到指定的远程服务器。具体解释如下&#xff1a; ssh&#xff1a;这是在 Unix-like 系统中…

k8s监控方案实践补充(二):使用kube-state-metrics获取资源状态指标

k8s监控方案实践补充&#xff08;二&#xff09;&#xff1a;使用kube-state-metrics获取资源状态指标 文章目录 k8s监控方案实践补充&#xff08;二&#xff09;&#xff1a;使用kube-state-metrics获取资源状态指标一、Metrics Server简介二、kube-state-metrics实战部署1. 创…