【负载均衡式在线OJ项目day4】编译运行功能整合及打包网络服务

news/2024/5/21 6:10:25

一.前言

前面两天完成了编译和运行两个子模块,今天的任务是完成CompileRun模块,它的任务如下:

  1. 解析来自客户端的Json字符串,反序列化提取编译运行需要的数据,即代码,时间限制和空间限制
  2. 把代码写入临时文件,形成编译的源文件
  3. 调用编译和运行模块,把各字段序列化构建Json字符串,返回给外部构建response的body部分
  4. 最后删除编译运行期间形成的临时文件

另外,在CompileServer.cpp文件将整套编译运行服务打包成网络服务,具体来说:

使用httplib库,使用post方法注册回调函数。当接收到客户端编译服务的请求时,会调用回调函数,request中的Json字符串交给CompileRun模块,得到Json字符串,用它构建response的body部分,然后返回给客户端

整个编译运行服务的逻辑如下:

 

二.设计思路

CompileRun:

首先对传入的inJson反序列化,提取出代码code,输入input,时间限制cpuLimit,空间限制memLimit。

然后在工具模块实现一个形成独一无二,不会产生冲突的文件名方法,可采用,毫秒级时间戳和原子级计数器组合来形成文件名。

接着将代码写入到该文件中,随后调用编译模块和运行模块。

如果编译报错,则填写状态码字段status和原因reason,直接把outJson返回;;如果由于其它原因导致失败,如打开文件,程序替换等,则是我们服务端内部出现问题,与用户无关,则填写status和reason,直接返回outJson;如果编译运行成功,则除了填写以上两个字段,还有标准输出stdout和标准错误stderr,然后返回Json字符串

最后还要使用unlink接口,将编译运行形成的.cpp,.compile_error, .exe,.stdin, .stdout, .stderr临时文件全部清空

 三.接口设计

参数:

1. const std::string &inJson, 输入型参数,来自客户端的Json字符串,包含以下字段:

         * 1.code:用户提交的代码

         * 2.input:用户提交的输入,不做处理

         * 3.cpuLimit:CPU限制时间(s)

         * 4.memLimit:虚拟内存限制大小(KB)

2. std::string *outJson, 输出型参数,将来发送给客户端的Json字符串,包含以下字段:

         * 必填:

         * 1.status:状态码

         *    0:运行成功 -1:代码为空 -2:编译错误 -3:未知错误 >0:收到信号异常终止

         * 2.reason:请求结果

         * 选填:

         * 3.stdout:程序输出运行结果

         * 4.stderr:错误结果

四.代码实现

CompileRun.hpp:

#pragma once#include <jsoncpp/json/json.h>
#include <string>
#include "Compiler.hpp"
#include "Runner.hpp"
#include "../Common/Log.hpp"
#include "../Common/Util.hpp"
namespace ns_compile_run
{using namespace ns_runner;using namespace ns_complier;using namespace ns_util;using namespace httplib;class CompileRun{public:/*********************** 参数:* 1.inJson:通过http来自client的json字符串* 2.outJson:输出型参数,将来要发送给client** inJson字段:* 1.code:用户提交的代码* 2.input:用户给自己提交代码对应的输入,不做处理* 3.cpuLimit:CPU限制时间(s)* 4.memLimit:虚拟内存限制大小(KB)** outJson字段:* 必填:* 1.status:状态码*    0:运行成功 -1:代码为空 -2:编译错误 -3:未知错误 >0:收到信号异常终止* 2.reason:请求结果* 选填:* 3.stdout:程序输出运行结果* 4.stderr:错误结果* *******************/static void start(const std::string &inJson, std::string *outJson){// 反序列化Json::Value inValue;Json::Reader reader;reader.parse(inJson, inValue);std::string code = inValue["code"].asString();std::string input = inValue["input"].asString();int cpuLimit = inValue["cpuLimit"].asInt();int memLimit = inValue["memLimit"].asInt();int status = 0;int runRet = 0;std::string fileName;if (code.size() == 0){status = -1; // 代码为空goto END;}fileName = FileUtil::uniqFileName();                     // 形成唯一文件名if (!FileUtil::writeFile(PathUtil::src(fileName), code)) // 形成临时源文件{status = -3; // 未知错误goto END;}if (!Compiler::compile(fileName)){status = -2; // 编译错误goto END;}runRet = Runner::run(fileName, cpuLimit, memLimit);if (runRet < 0){status = -3; // 未知错误}else if (runRet > 0){status = runRet; // 异常终止}else{status = 0; // 运行成功}END:Json::Value outValue;outValue["status"] = status;outValue["reason"] = statusToDesc(status, fileName);if (status == 0) // 编译运行成功{std::string _stderr;std::string _stdout;FileUtil::readFile(PathUtil::stderr(fileName), &_stderr, true);FileUtil::readFile(PathUtil::stdout(fileName), &_stdout, true);outValue["stderr"] = _stderr;outValue["stdout"] = _stdout;}Json::StyledWriter writer;*outJson = writer.write(outValue);//removeTmpFiles(fileName);}/**************************** 功能:根据状态码返回相应的reason* ************************/static std::string statusToDesc(int status, const std::string &fileName){std::string desc;switch (status){case 0:desc = "编译运行运行成功";break;case -1:desc = "代码为空";break;case -2:FileUtil::readFile(PathUtil::complieError(fileName), &desc, true);break;case -3:desc = "未知错误";break;case SIGFPE: // 8desc = "浮点错误";break;case SIGXCPU: // 24desc = "运行超时";break;case SIGABRT: // 6desc = "内存超出限制";break;default:desc = "未知";break;}return desc;}/************************* 功能:删除编译运行形成的临时文件* 最多有:.cpp, .compile_error, .exe, .stdin, .stdout, .stderr* ******************/static void removeTmpFiles(const std::string &fileName){std::string _src = PathUtil::src(fileName);std::string _compileError = PathUtil::complieError(fileName);std::string _exe = PathUtil::exe(fileName);std::string _stdin = PathUtil::stdin(fileName);std::string _stdout = PathUtil::stdout(fileName);std::string _stderr = PathUtil::stderr(fileName);if (FileUtil::isFileExists(_src)){unlink(_src.c_str());}if (FileUtil::isFileExists(_compileError)){unlink(_compileError.c_str());}if (FileUtil::isFileExists(_exe)){unlink(_exe.c_str());}if (FileUtil::isFileExists(_stdin)){unlink(_stdin.c_str());}if (FileUtil::isFileExists(_stdout)){unlink(_stdout.c_str());}if (FileUtil::isFileExists(_stderr)){unlink(_stderr.c_str());}}};
}

工具模块用到的一些方法:

#pragma once
#include <string>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/time.h>
#include <atomic>
#include <fstream>
namespace ns_util
{class TimeUtil{public:/*********** 功能:当前毫秒级时间* ********/static std::string getTimeMs(){struct timeval time;gettimeofday(&time, nullptr);return std::to_string(time.tv_sec + time.tv_usec / 1000);}};class FileUtil{public:/**************** 功能:判定文件是否存在* 参数:pathName是完整文件名* 如1234-> ./tmp/1234.stderr***************/static bool isFileExists(const std::string &pathName){struct stat st;if (stat(pathName.c_str(), &st) == 0){// 获取属性成功,说明文件存在return true;}return false;}/************************* 参数:* 1.target:带路径和后缀的完整文件名* 2.content:要写入的内容* *********************/static bool writeFile(const std::string& target, const std::string& content){std::ofstream out(target.c_str());if (!out.is_open()){return false;}out.write(content.c_str(), content.size());return true;}/************************* 参数:* 1.target:带路径和后缀的完整文件名* 2.content:输入型参数,把文件内容写到它里面* 3.keep:是否保留'\n'(getline不会读取换行符)* *********************/static bool readFile(const std::string& target, std::string* content, bool keep = false){std::ifstream in(target.c_str());if (!in.is_open()){return false;}std::string line;while (std::getline(in, line)){line += keep ? "\n" : "";(*content) += line;}return true;}/****************** 功能:用时间和原子计数器生成一个独一无二,不产生冲突的文件名* ******************/static std::string uniqFileName(){static std::atomic<int> id(0);std::string fileName = TimeUtil::getTimeMs();fileName += "_";fileName += to_string(id);id++;return fileName;}};
};

CompileServer.cpp:

#include <iostream>
#include <string>
#include <cstdlib>
#include "../Common/httplib.h"
#include "CompileRun.hpp"
#include "../Common/Util.hpp"
#include "../Common/Log.hpp"using namespace ns_compile_run;
using namespace httplib;
using namespace ns_log;void Usage(const char* proc)
{std::cout << proc << "serverPort" << std::endl;
}
int main(int argc, char* argv[])
{if (argc != 2){Usage(argv[0]);return 1;}Server svr;svr.Post("/compile_and_run", [](const Request& req, Response& resp){std::string inJson = req.body;if (!inJson.empty()){std::string outJson;CompileRun::start(inJson, &outJson);resp.set_content(outJson, "application/json;charset=utf-8");}}); //注册回调方法svr.listen("0.0.0.0", atoi(argv[1])); //启动网络服务return 0;
}

五.备注

  1. httplib是一个只需要包含头文件,而无需安装动态库的“only header”库,它的方法定义都在httplib.h中了,我们只需将它拷贝到项目目录下即可包含使用
  2. 要使用httplib,必须使用高版本gcc编译器(7,8,9),否则编译或者运行时会出现问题
  3. 想要对编译运行模块测试,可以使用postman工具向服务端发送request

http://www.mrgr.cn/p/12057120

相关文章

盘多啦使用教程

为什么要做副业 不管是在疫情前还是疫情后,相对于普通人来说,本质工作仅仅是能够负担起正常的生活开支。反而副业才是你真正的收入。有些人会通过股票、基金等等金钱上的投资去赚取这部分的收入,但是往往伴随着高风险、高收益。那有没有什么副业是“安全,无副作用”的呢,答…

25-有参转录组实战11-上传转录组到NCBI

上传转录组到NCBI登录NCBI>点击submit>选SRA>选Project>点New submission 1 SUBMITTER 填写名字,邮件,no group,学校学院,街道邮编国家,continue 2 GENERAL INFO 填no BioProject, no BioSample,立马释放数据。 3 PROJECT INFO 填个title和description,no…

项目冲刺day3

这个作业属于哪个课程 软工4班这个作业要求在哪里 作业要求1.会议1. 照片 时间冲突,采用微信聊天方式2. 昨日已完成: 完成登录、注册功能,部分完成用户中心功能3.今天计划完成的工作 用户中心功能、订单管理功能4.工作中遇到的困难 沟通和信息共享并不总是顺利。这导致了一些…

MySQL面试必备二之binlog日志

本文首发于公众号:Hunter后端 原文链接:MySQL面试必备二之binlog日志关于 binlog,常被问到几个面试问题如下:binlog 是什么 binlog 都记录什么数据 binlog 都有哪些类型,都有什么特点 如何使用 binlog 恢复数据 binlog 都有哪些作用 binlog 属于逻辑日志还是物理日志基于上…

Transformers中加载预训练模型的过程剖析

使用HuggingFace的Transformers库加载预训练模型来处理下游深度学习任务很是方便,然而加载预训练模型的方法多种多样且过程比较隐蔽,这在一定程度上会给人带来困惑。因此,本篇文章主要讲一下使用不同方法加载本地预训练模型的区别、加载预训练模型及其配置的过程,藉此做个记…

Android GPU渲染屏幕绘制显示基础概念(1)

Android GPU渲染屏幕绘制显示基础概念&#xff08;1&#xff09; Android中的图像生产者OpenGL&#xff0c;Skia&#xff0c;Vulkan将绘制的数据存放在图像缓冲区中&#xff0c;Android中的图像消费SurfaceFlinger从图像缓冲区将数据取出&#xff0c;进行加工及合成。 Surface…

开源RAG框架汇总

前言 本文搜集了一些开源的基于LLM的RAG(Retrieval-Augmented Generation)框架,旨在吸纳业界最新的RAG应用方法与思路。如有错误或者意见可以提出,同时也欢迎大家把自己常用而这里未列出的框架贡献出来,感谢~ RAG应用框架RAGFlow项目地址:https://github.com/infiniflow/…

kali中arp欺骗,连上校园网断舍友的网

首先kali的配置: 参考网站:https://jingyan.baidu.com/article/2c8c281d145cf44108252a97.html 然后下载arpspoof插件: apt-get install dsniff然后一条命令: arpspoof -i eth0 -t 受害者的ip 网关//这个网关是你自己连上校园网的那个网关

Scrum冲刺4--5.10

Scrum冲刺4--5.10这个作业属于哪个课程 软件工程这个作业要求在哪里 团队项目这个作业的目标 进行敏捷冲刺,熟悉团队合作开发前端仓库 前端后端仓库 后端每次冲刺日志索引时间 博客5.7 Day1ᕙ(`▿)ᕗ5.8 Day2ᕙ(• ॒ ູ•)ᕘ5.9 Day3(˚ ˃̣̣̥᷄⌓˂̣̣̥᷅ )5.10 Day4 (…

Navicat Data Modeler Ess for Mac:强大的数据库建模设计软件

Navicat Data Modeler Ess for Mac是一款专为Mac用户设计的数据库建模与设计工具&#xff0c;凭借其强大的功能和直观的界面&#xff0c;帮助用户轻松构建和管理复杂的数据库模型。 Navicat Data Modeler Ess for Mac v3.3.17中文直装版下载 这款软件支持多种数据库系统&#x…

敏捷冲刺day3--数字工匠队

这个作业属于哪个课程 软件工程这个作业的要求是什么 项目冲刺这个作业的目标 冲刺日志3站立式会议照片工作困难 处理任务时遇到一些问题需要上网学习花费时间 昨日完成工作 部分登录界面前后端处理代码 今日计划工作 继续完成登录界面前后端处理 项目燃尽图每日总结 邹嘉伟:继…

中国地面气候资料日值数据获取方式

数据简介 环境气象数据服务平台提供了全国大约2100个点位&#xff0c;2000年至2023年的逐日数据。包括气温、气压、湿度、风、降水等要素。 数据基于ECMWF reanalysis-era5-land、reanalysis-era5-single-levels 以及中国2100站点地面气候资料日值观测数据&#xff0c;使用机器…

第 3 篇 Scrum 冲刺博客

每天举行站立式会议 昨天已完成的工作: 今天计划完成的工作: 工作中遇到的困难: 项目燃尽图代码/文档签入记录项目展示每日每人总结 李健宇:明天加油。 陈彦煤:尽力完成,克服困难。

如何使用client-go构建pod web shell

代码示例及原理 原理是利用websocket协议实现对pod的exec登录&#xff0c;利用client-go构造与远程apiserver的长连接&#xff0c;将对pod容器的输入和pod容器的输出重定向到我们的io方法中&#xff0c;从而实现浏览器端的虚拟终端的效果消息体结构如下 type Connection stru…

【c++算法篇】双指针(下)

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;算法笔记仓 朋友们大家好啊&#xff0c;本篇文章我们来到算法的双指针的第二部分 目录 1.有效三角形的个数2.查找总价格为目标值的两个商品3.三数之和4.四数之和5.双指针常见场景总结 1.有效三角形…

移动机器人系统与技术:自动驾驶、移动机器人、旋翼无人机

这本书全面介绍了机器人车辆的技术。它介绍了道路上自动驾驶汽车所需的概念。此外&#xff0c;读者可以在六足机器人的构造、编程和控制方面获得宝贵的知识。 这本书还介绍了几种不同类型旋翼无人机的控制器和空气动力学。它包括各种旋翼推进飞行器在不同空气动力学环境下的模…

ThreeJS:本地部署官网文档与案例

部署方式 部署之前请确保已经配置好node.js环境。 1. 下载ThreeJS源码 ThreeJS的GitHub地址&#xff1a;GitHub - mrdoob/three.js: JavaScript 3D Library.&#xff0c;可以简单查看ThreeJS当前版本&#xff1a;r164&#xff0c; 我们可以选择对应的版本&#xff08;此处为r1…

10秒以上无错误!猫态量子比特稳定性达到新水平

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨 浪味仙 排版丨沛贤 深度好文&#xff1a;1200字丨5分钟阅读 摘要&#xff1a;与涉及超导电路的其他量子比特设计相比&#xff0c;使用猫态量子比特可能会“将用于纠错的量子比特数量减少到…

SpringBoot整合Mybatis时mapper文件和xml文件的位置

xml文件放在resources下看下我的项目目录2.由于放在resurces下就无法扫描到xml文件,所以就需要在配置文件配置--mapper文件位置 mybatis.mapper-locations=classpath:mapper/*.xml 或 mybatis.mapper-locations=classpath:/mapper/*.xmlxml和mapper文件放在一起我的项目目录但…

Flume进阶

目录 第1关&#xff1a;拦截器的使用 第2关&#xff1a;自定义拦截器 第1关&#xff1a;拦截器的使用 代码文件&#xff1a; # Define source, channel, sink #agent名称为a1# Define source #source类型配置为avro,监听8888端口&#xff0c;后台会自动发送数据到该端口 #拦截后…