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

负载均衡式的在线OJ项目编写(一)

一.项目演示

本项目的功能为一个在线的OJ,实现类似leetcode的题目列表、在线提交、编译、运行等功能。

二.所用到的技术与开发环境

所⽤技术:

• C++ STL 标准库

• Boost 准标准库(字符串切割)

 • cpp-httplib 第三⽅开源⽹络库

• ctemplate 第三⽅开源前端⽹⻚渲染库

• jsoncpp 第三⽅开源序列化、反序列化库

• 负载均衡设计

• 多进程、多线程

• MySQL C connect

• Ace前端在线编辑器(了解)

 • html/css/js/jquery/ajax (了解)

开发环境

• Centos 7 云服务器

• vscode

• Mysql Workbench

三.项⽬宏观结构

 我们的项⽬核⼼是三个模块

1. comm :公共模块 

2. compile_server :编译与运⾏模块

3. oj_server :获取题⽬列表,查看题⽬编写题⽬界⾯,负载均衡,其他功能

I.leetcode 结构

• 只实现类似 leetcode 的题⽬列表+在线编程功能

II.我们的项⽬宏观结构

III.编写思路

1. 先编写 compile_server

2. oj_server

3. version1 基于⽂件版的在线OJ 

4. 前端的⻚⾯设计

5. version2 基于 MySQL 版的在线OJ

四.compiler 服务设计

提供的服务:编译并运⾏代码,得到格式化的相关的结果

还需要设计一个makefile自动编译模块

compile_server:compile_server.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -rf compile_server

开始设计compile模块(只负责编译代码)

我们还需要加一个temp文件夹来保存代码文件

compile代码的大致思路(不完整的)

#pragma once//只负责代码的编译#include <iostream>
#include <unistd.h>namespace ns_compiler
{class Compiler{public:Compiler(){}~Compiler(){}//返回值: 编译成功: true, 否则: false//输入参数: 编译文件名//filename -> name//name -> ./temp/name.cpp//name -> ./temp/name.exe//name -> ./temp/name.stderr//文件路径存储在temp路径下static bool Compile(const std::string& file_name){pid_t res = fork();if(res < 0){//创建子进程失败return false;}else if(res == 0){//子进程 执行编译代码//调用编译器,执行编译//g++ -o target src -std=c++11execlp("g++","-o")}else{//父进程}}};
}

可以发现我们不仅要生成.exe文件和.stderr文件,后续运行文件也要用到路径拼接,所以我们可以在comm(公共模块)中设计一个路径拼接util类

//路径拼接大致思路代码#pragma once#include <iostream>
#include <string>namespace ns_util
{class PathUtil{public://构建源文件路径 + 后缀的完整文件名static std::string Src(const std::string & file_name){}//构建可执行程序的完整路径 + 后缀static std::string Exe(const std::string & file_name){}//构建该程序对应的标准错误的完整路径 + 后缀static std::string Stderr(const std::string & file_name){}};
}

util类的具体实现

#pragma once#include <iostream>
#include <string>namespace ns_util
{const std::string temp_path = "./temp/";class PathUtil{public:static std::string AddSuffix(const std::string& file_name,const std::string& suffix){std::string path_name = temp_path;path_name += file_name;path_name += suffix;return path_name;}//构建源文件路径 + 后缀的完整文件名//name -> ./temp/name.cppstatic std::string Src(const std::string & file_name){return AddSuffix(file_name,".cpp");}//构建可执行程序的完整路径 + 后缀static std::string Exe(const std::string & file_name){return AddSuffix(file_name,".exe");}//构建该程序对应的标准错误的完整路径 + 后缀static std::string Stderr(const std::string & file_name){return AddSuffix(file_name,".stderr");}};
}

compile的具体实现

static bool Compile(const std::string& file_name){pid_t pid = fork();if(pid < 0){//创建子进程失败return false;}else if(pid == 0){//子进程 执行编译代码//调用编译器,执行编译//g++ -o target src -std=c++11execlp("g++","-o",PathUtil::Exe(file_name).c_str(),\PathUtil::Src(file_name).c_str(),"-std=c++11",nullptr);exit(1);}else{//父进程//nullptr:表示不关心子进程退出的状态//0:表示阻塞等待waitpid(pid,nullptr,0);//编译是否成功,就看是否形成了可执行的程序if(FileUtil::IsFileExists(PathUtil::Exe(file_name))){return true;}}return false;}

要判断文件是否存在,这个函数使用的频率也较高(也放到comm模块中)

新的判断文件是否存在的接口介绍

class FileUtil{public:static bool IsFileExists(const std::string& path_name){struct stat st;if(stat(path_name.c_str(),&st) == 0){//获取属性成功,文件已经存在return true;}return false;}};

编译成功已经写好了,那编译出错呢?

编译出错的话,我们将错误重定向到标准错误文件中写入错误信息

创建并打开文件

重定向接口

五.log日志服务的编写

上面初步完成了我们编译的过程,必要时还是要打印日志的(所以我们来写一个日志功能)

#pragma once#include <iostream>
#include <string>
#include "util.hpp"namespace ns_log
{using namespace ns_util;//日志等级enum {INFO,DEBUG,WARNING,ERROR,FATAL};inline std::ostream& Log(const std::string& level,const std::string& file_name,int line){//添加日志等级std::string message = "[";message += level;message += "]";//添加报错文件名称message += "[";message += file_name;message += "]";//添加报错行message += "[";message += std::to_string(line);message += "]";//日志时间戳message += "[";message += TimeUtil::GetTimeStamp();message += "]";//cout本质 内部是包含缓存区的//没有endl就不会将缓存区内部的内容全部刷新出来std::cout << message;return std::cout;}// LOG(INFO) << "message" << "\n"//开放式日志#define LOG(level) Log(#level,__FILE__,__LINE__)}

六.测试编译模块

还有一个比较重要的一点,在vsocde创建的文件,是root的,所有不能用其他普通用户去执行(会出错)

报错测试

补充

七.运行功能开发

子进程来执行代码,父进程来进行等待

可以思考以下的问题:

            /******************************

            * 程序运行:

            * 1. 代码跑完,结果正确

            * 2. 代码跑完,结果不正确

            * 3. 代码没跑完,异常了

            * Run模块需要考虑代码跑完,结果正确与否吗??? 不考虑

            * 结果正确与否: 是由我们的测试用例来决定的!

            * 我们只考虑: 是否正确运行完毕

            *

            *

            * 我们必须知道可执行程序是谁? ./temp/code.exe

            * 一个程序在默认启动的时候

            * 标准输入: 不处理(用户自测)

            * 标准输出: 程序运行完成,输出结果是什么

            * 标准错误: 运行时错误信息

            * 全部写道同名文件中

            ******************************/

#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>#include "../comm/log.hpp"
#include "../comm/util.hpp"namespace ns_runner
{using namespace ns_log;using namespace ns_util;class Runner{public:Runner(){}~Runner(){}public://指明文件名即可,不需要代理路径,不需要带后缀/*********************************** 返回值 > 0:程序异常了,退出时受到了信号,返回值就是对应的信号编号* 返回值 = 0:正常运行完毕的,结果保存到了对应的临时文件中* 返回值 < 0:内部错误* *********************************/static int Run(const std::string & file_name){/******************************* 程序运行:* 1. 代码跑完,结果正确* 2. 代码跑完,结果不正确* 3. 代码没跑完,异常了* Run模块需要考虑代码跑完,结果正确与否吗??? 不考虑* 结果正确与否: 是由我们的测试用例来决定的!* 我们只考虑: 是否正确运行完毕* * * 我们必须知道可执行程序是谁? ./temp/code.exe* 一个程序在默认启动的时候* 标准输入: 不处理(用户自测)* 标准输出: 程序运行完成,输出结果是什么* 标准错误: 运行时错误信息* 全部写道同名文件中******************************/std::string _execute = PathUtil::Exe(file_name);std::string _stdin   = PathUtil::Stdin(file_name);std::string _stdout  = PathUtil::Stdout(file_name);std::string _stderr  = PathUtil::Stderr(file_name);umask(0);int _stdin_fd = open(_stdin.c_str(),O_CREAT | O_RDONLY,0644);int _stdout_fd = open(_stdout.c_str(),O_CREAT | O_WRONLY,0644);int _stderr_fd = open(_stderr.c_str(),O_CREAT | O_WRONLY,0644);if(_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0){LOG(ERROR) << "运行时打开标准文件失败" << "\n";return -1;//代表打开文件失败}pid_t pid = fork();if(pid < 0){LOG(ERROR) << "运行时创建子进程失败" << "\n";close(_stdin_fd);close(_stdout_fd);close(_stderr_fd);return -2;//代表创建子进程失败}else if(pid == 0){dup2(_stdin_fd,0);dup2(_stdout_fd,1);dup2(_stderr_fd,2);execl(_execute.c_str()/*我要执行谁*/,_execute.c_str()/*在命令行上怎么执行*/,nullptr);exit(1);}else{close(_stdin_fd);close(_stdout_fd);close(_stderr_fd);int status = 0;waitpid(pid,&status,0);LOG(INFO) << "运行完毕, info: " << (status & 0x7F) << "\n";//程序运行异常,一定是因为受到了信号return status & 0x7F; //将程序是否受到异常(信号 1-30)进行返回}}};
}

八.测试运行模块

#include "compiler.hpp"
#include "runner.hpp"using namespace ns_compiler;
using namespace ns_runner;int main()
{std::string code = "code";Compiler::Compile(code);Runner::Run(code);return 0;
}

编译完之后,就直接运行程序

未完待续

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

相关文章:

  • SQL-视图
  • 网站备案 链接成都市成华区建设局网站
  • 【数据结构前置知识】集合框架
  • 度学习中的关键工具与数据处理方法
  • 网站制作案例策划洛阳百事通文化传播有限公司
  • 大模型微调——LoRA微调
  • Mysql常用调优参数
  • MySQL 概述及其在 C/C++ 开发中的应用价值
  • 新乡手机网站建设电话wordpress rest post
  • OpenAI与 Nvidia合作意味着什么
  • 做淘宝详情页好的网站wordpress制作lp
  • Maven-继承与聚合
  • 中国建设银行淮南分行网站设计官网的
  • 如何分析软件需求中的DFX需求?
  • 网站开发需求分析参考文献软件市场下载
  • MySQL笔记---对数据库(database)的操作
  • 文创网站江西合创建设工程有限公司 网站
  • 【Leetcode hot 100】124.二叉树中的最大路径和
  • 仙桃做网站找谁优化大师怎么卸载
  • 网站用户模板做网站需要的东西
  • 自定义类型之联合体、枚举
  • 计算机——信息素养和信息安全
  • php 整个网站变量实惠的制作网站
  • 不同路劲 III dfs解决
  • 海沧网站建设如何做好网站首页
  • PHP女程序猿学习Java的Day-12
  • 电商运营如何提升自身技能素质?
  • 网站开发四川网站的用户运营值得做吗
  • Android中开一个线程是ULT还是KLT
  • 动态IP的适用业务场景