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

项目——负载均衡OJ

项目要实现的一个整体的功能:
编写一个在线OJ网络服务器,只实现类似 leetcode 的题目列表+在线编程功能

项目宏观结构:

Oj服务器在收到提交的代码时,把代码负载均衡的选择发送给其他几个编译与运行服务器去编译运行代码,判断代码的编译运行结果

  1. comm : 公共模块
  2. compile_server : 编译与运行模块
  3. oj_server : 获取题目列表,查看题目编写题目界面,负载均衡,其他功能
    在这里插入图片描述

所用技术与开发环境

所用的技术:

C++ STL 标准库
Boost 准标准库(字符串切割)
cpp-httplib 第三方开源网络库
ctemplate 第三方开源前端网页渲染库
jsoncpp 第三方开源序列化、反序列化库
负载均衡设计
多进程、多线程
MySQL C connect
Ace前端在线编辑器(了解)
html/css/js/jquery/ajax (了解)

所用环境

ubuntu 20.04 云服务器
vscode
MySQL Workbench

项目编写思路:

  1. 先编写 compile_server
  2. oj_server
  3. version1 基于文件版的在线OJ
  4. 前端的页面设计
  5. version2 基于 MySQL 版的在线OJ

编译运行服务器

先编写 compile_server

1、compile.hpp实现编译功能

通过类的封装实现,创建对象来调用里面的函数来实现编译功能
接收到一个代码文件,然后把他编译为可执行代码,如果出错把错误放到创建的重定向临时错误文件中。
编译成功返回true错误返回false
在这里插入图片描述

过程中还实现了:拼接文件路径和后缀、判断文件是否存在、获取时间戳、日志

公共模块
util.hpp(工具类):

1、文件路径功能,在编译、运行的函数中,参数只需要传入一个文件名,路径和后缀根据不同功能而拼接,就能找到打开相应的文件

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;}//编译时需要的临时文件//构建源文件路径+后缀的完整文件名static 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 CompilerError(const std::string &file_name){return AddSuffix(file_name,".compile_error");}//运行时需要的临时文件//标准输入static std::string Stdin(const std::string &file_name){return AddSuffix(file_name,".stdin");}//标准输出static std::string Stdout(const std::string &file_name){return AddSuffix(file_name,".stdout");}//构建该程序运行对应的标准错误完整的路径+后缀名static std::string Stderr(const std::string &file_name){return AddSuffix(file_name,".stderr");}};

2、文件工具: class FileUtil
①、判断文件是否存在 ——用于编译完成之后,判断有没有生成可执行文件,生成了就成功,没有就失败
系统调用stat获取文件的属性,获取成功返回0;否则失败
在这里插入图片描述
3、时间工具:
获取时间戳共日志使用,获取毫秒级时间戳供原子性形成文件名使用
在这里插入图片描述

日志功能

通过out<<流的特性
在这里插入图片描述
以上准备工作都做了,就可以编写编译功能compile.hpp
标准错误文件重定向,子进程程序替换实现编译,形成.exe可执行文件判断成功与否
在这里插入图片描述

实现运行功能模块

1、传入的值:file_name、cpu_limit、cpu_limit 返回值为Int
首先根据file_name 调用拼接函数,分别拼接出exe、in、out、err、可执行文件,标准输入,标准输出,标准错误的文件名,因为exe文件是编译的时候就创建的,所以我们不用再创建,其他的文件通过打开没有就创建的方式创建。然后重定向为,标准输入,标准输出,标准错误。之后再根据传入的
cpu_limit、cpu_limit设置资源限制,然后创建子进程替换,执行exe文件,父进程等待,执行结果成功会放入.out文件,失败会放入.err文件中。运行模块不关心结果,只关心退出时父进程接收到的信号,信号>0,运行处异常,信号==0运行成功,return -1服务器内部错误

图:
在这里插入图片描述

首先运行后的结果要重定向到标准输出文件,运行出错的结果要重定向到标准错误,

//运行编译成功的程序
/******************************* *
*1、代码跑完,结果正确
*2、代码跑完,结果不正确
*3.代码没跑完,异常了
*思考:Run模块不需要考虑代码跑完,结果的正确与否
*结果的正确与否,是由调用层的测试用例决定的
*所以此模块我们只需判断是否正确运行完毕*我们必须知道可执行程序是谁——通过拼接就能找到
*程序在启动时,默认打开的文件:
*标准输入:不做处理
*标准输出:运行完后输出的结果
*标准错误:运行是的错误信息
******************************* */
//拼接出各种同名不同缀的文件路径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;}

设置资源限制——防止用户提交的代码死循环,或者是恶意代码,—— setrlimit资源限制来保护服务器

//提供设置进程占用资源大小的接口static  void SetProcLimit(int cpu_limit,int mem_limit){//设置CPU限制时长struct rlimit cpu_rlimit;cpu_rlimit.rlim_cur=cpu_limit;cpu_rlimit.rlim_max=RLIM_INFINITY;//硬约束无约束setrlimit(RLIMIT_CPU,&cpu_rlimit);//设置内存大小限制struct rlimit mem_rlimit;mem_rlimit.rlim_cur=mem_limit*1024;//*1024转化为KBmem_rlimit.rlim_max=RLIM_INFINITY;setrlimit(RLIMIT_AS,&mem_rlimit);}

程序替换运行代码:运行完成结果保存在重定向的标准输入中,运行时出异常或者资源约束超出限制收到信号,

 //程序替换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;

总代码:
在这里插入图片描述

实现编译运行compile_run.hpp

在这里插入图片描述

服务器收到一个请求,请求的正文里面就是用户提交的代码(也有可能有参数input
),编译运行服务要把他序列化后拿到代码做编译运行,然后结果字段要反序列化后发送回去
在这里插入图片描述
包含进第三方开源库jsoncpp:用于请求和响应的序列化反序列化
在这里插入图片描述

反序列化后拿到代码,然后形成唯一文件名,把代码放入文件中
形成唯一文件名函数方法放在工具模块中:形成名字后,要拼接好路径和后缀,然后写入,写入的时候会自动创建文件
在这里插入图片描述

形成唯一文件名:毫秒级时间戳+原子性递增的唯一值:
在这里插入图片描述
把代码写入到文件.cpp中
在这里插入图片描述
编译时出错的信息在编译时重定向的文件中,运行完成后的结果在重定向的标准输出文件中,所以也要有读文件的操作
在这里插入图片描述
一次编译运行服务完后,清理形成的临时文件
系统调用unlink直接删除文件,但是要清理的文件是不确定的,有可能编译时就出现了错误,那么运行时产生的文件就不存在,所以在删除文件的时候要先判断文件是否存在——这个功能在编译模块中判断可执行文件存不存在的时候就写好了,
所以判断存不存在,存在直接调用unlink直接删除文件
在这里插入图片描述
在这里插入图片描述

引入httplib第三方库,把编译运行服务打包为网络服务

httplib的作用:可以为我们自动创建套接字网络服务,以及构建请求和响应报头,而且是支持多客户端访问的服务,能够自动创建线程去完成每一个请求
我们要做的就只是,把他请求中的正文(json串)拿出来,然后编译运行后得到的out_json传作为响应的正文,他就会自动侯建响应发送回去
在这里插入图片描述
利用postman工具测试编译运行服务器
在这里插入图片描述

基于MVC 结构的oj 服务设计

——本质上就是一个网站

  1. 获取首页,用题目列表充当
  2. 编辑区域页面
  3. 提交判题功能(编译并运行)

M: Model,通常是和数据交互的模块,比如,对题库进行增删改查(文件版,MySQL)
V: view, 通常是拿到数据之后,要进行构建网页,渲染网页内容,展示给用户的(浏览器)
C: control, 控制器,就是我们的核心业务逻辑

用户请求的服务路由功能oj_server.cc

既然是一个服务器功能,那么首先也是要引用httplib第三方库,很创建一个Server svr 服务器

首先如果客户端URL直接根据ip和port访问的话,返回一个首页网页
在这里插入图片描述
在这里插入图片描述
ojserver的主要三个功能
在这里插入图片描述

设计文件版题库:

在这里插入图片描述

model功能,提供对数据的操作:和数据进行交互,对外提供访问数据的接口

题库是设计在文件中的,所以需要这个模块帮我们把他加载到内存,
在这里插入图片描述

首先要把文件中的数据读上来,那么就要有一个结构体,里面包含一些字段,来保存这些信息
在这里插入图片描述

加载题目
如何让我们通过题号找到对应的题目呢,这里就要用一个map结构把编号,题目信息。关联起来
在这里插入图片描述
在这里插入图片描述

获取所有题目的接口:
循环遍历map,然后把map中的second插入到vector中,这样数组中就保存了所有的题目信息
在这里插入图片描述

根据题号获取单个题目信息的接口
因为是存放在map中的,左移题号就是key值,所以直接在map中根据题号查找,然后把对应的second,放到输出参数Questions中
在这里插入图片描述
此外字符串的切割,使用boost库来实现的
在这里插入图片描述

control 逻辑控制模块

功能1:获取题目信息,构建网页,返回给oj_server

在这里插入图片描述

那么数据如何渲染成html网页的呢,这里就用到吗ctemplate 第三方开源前端网页渲染库
一个小demo来介绍他是如何使用的:
在这里插入图片描述
当然html网页渲染功能肯定是不会直接写在控制模块中,而是写在oj_view图片渲染功能中的

oj_view 网页渲染模块

获取所有题目,形成列表html网页
首先一定是oj_server收到了获取所有题目的一个请求:
在这里插入图片描述
然后在控制模块中获取所有题目的信息,然后渲染,返回
在这里插入图片描述
view模块渲染所有题目——ctemplate 第三方开源前端网页渲染库
在这里插入图片描述
最终页面显示:
在这里插入图片描述

获取单个题目,并渲染成网页——但是这里还是一个简单的网页,还没有提交代码,整体的框架也有点丑
现在先这样之后把控制模块中的判题功能写完,在优化这个前端网页。
在这里插入图片描述

剩下的一个功能就是提交代码,编译运行了

但是我们是负载均衡的选择多台主机来编译的

所以在控制模块中,还应该有一个负载均衡选择的模块

既然要选择那么我们就要把所有主机管理起来(结构体),然后去选择负载最小的那个

主机的结构体:
在这里插入图片描述
负载均衡选择:首先要知道有哪些主机,那么我们就要去主机配置文件中按行读取,然后把内个主机都设为一个结构,在保存到数组中,
然后根据每个主机的负载,循环遍历每个主机找到负载最小的那个主机,把id(数组下标),和主机信息返回回去
在这里插入图片描述

用户提交代码,根据题号拿到相应的题目信息,把用户提交的代码和对应的测试用例拼接,反序列化后发送给编译运行服务,最终得到结果

在这里插入图片描述
还有一个小问题:因为我们的题目信息中预设代码和测试用例是分开写的,为了不报错我们写测试用例的时候是有一个包含预设代码头文件的操作的,但是拼接的时候预设代码是直接作为用户提交的代码被拼接上的,不是从headr.cpp中拿的,所以不用包含头文件。所以我们是用到了一个条件编译的。
在这里插入图片描述
在这里插入图片描述

至此所有的后端服务都已经编写完成,现在来编译编译界面的一个网页:ACE在线编译器 编译页面

总和测试

相关文章:

  • 【Qt开发】QT6.5.3安装方法(使用国内源)亲测可行!!!
  • Prometheus与Grafana入门:从安装到基础监控的完整指南
  • 海信发布以旧换新举措,补贴力度、补贴链路、服务体验全面升级
  • 通过用例演示如何反向截取QString对象的子串
  • Python 算法交易实验88 QTV200日常推进-关于继续前进的思考
  • 打破AI壁垒-降低AI入门门槛
  • 【扇贝编程】使用Selenium模拟浏览器获取动态内容笔记
  • 【苍穹外卖】Day 6 HttpClient、wx小程序
  • 用Boot写mybatis的增删改查
  • AI prompt(提示词)
  • 【Python报错已解决】 AttributeError: ‘move_to‘ requires a WebElement
  • 【论文阅读】DETRs Beat YOLOs on Real-time Object Detection
  • [项目][CMP][直接向堆申请页为单位的大块内存]详细讲解
  • Spring 中使用的设计模式全面解析
  • 自动化表格处理的革命:智能文档系统技术解析
  • hexo+github搭建个人博客
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Fabric架构演变之路
  • HTML中设置input等文本框为不可操作
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • javascript 总结(常用工具类的封装)
  • MySQL的数据类型
  • Python利用正则抓取网页内容保存到本地
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • socket.io+express实现聊天室的思考(三)
  • 回顾2016
  • 基于 Babel 的 npm 包最小化设置
  • 经典排序算法及其 Java 实现
  • 移动端解决方案学习记录
  • gunicorn工作原理
  • Java性能优化之JVM GC(垃圾回收机制)
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • ​学习一下,什么是预包装食品?​
  • ()、[]、{}、(())、[[]]命令替换
  • (2)STM32单片机上位机
  • (Redis使用系列) SpringBoot中Redis的RedisConfig 二
  • (ZT)一个美国文科博士的YardLife
  • (四)activit5.23.0修复跟踪高亮显示BUG
  • (万字长文)Spring的核心知识尽揽其中
  • (一)u-boot-nand.bin的下载
  • (转载)OpenStack Hacker养成指南
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • (自适应手机端)响应式新闻博客知识类pbootcms网站模板 自媒体运营博客网站源码下载
  • .NET Core 版本不支持的问题
  • .NET 材料检测系统崩溃分析
  • .NET 快速重构概要1
  • .Net的C#语言取月份数值对应的MonthName值
  • .NET项目中存在多个web.config文件时的加载顺序
  • @property python知乎_Python3基础之:property
  • @RequestBody的使用
  • @vue-office/excel 解决移动端预览excel文件触发软键盘
  • [.NET]桃源网络硬盘 v7.4
  • [1]从概念到实践:电商智能助手在AI Agent技术驱动下的落地实战案例深度剖析(AI Agent技术打造个性化、智能化的用户助手)
  • [AutoSar]状态管理(五)Dcm与BswM、EcuM的复位实现