项目——负载均衡OJ

news2024/9/20 8:51:55

项目要实现的一个整体的功能:
编写一个在线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转化为KB
            mem_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在线编译器 编译页面

总和测试

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

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

相关文章

springboot+vue+mybatis计算机毕业设计气象数据分析与可视化系统+PPT+论文+讲解+售后

随着互联网技术不断地发展&#xff0c;网络与大数据成为了人们生活的一部分&#xff0c;而气象数据分析与可视化系统 作为网上应用的一个全新的体现&#xff0c;由于其特有的便捷性&#xff0c;已经被人们所接受。目前主流的气象数据分析与可视化系统 服务不仅不明确并且管理…

滑动窗口系列(同向双指针)/9.7

新的解题思路 一、三数之和的多种可能 给定一个整数数组 arr &#xff0c;以及一个整数 target 作为目标值&#xff0c;返回满足 i < j < k 且 arr[i] arr[j] arr[k] target 的元组 i, j, k 的数量。 由于结果会非常大&#xff0c;请返回 109 7 的模。 输入&…

AMEYA360:村田量产用于汽车市场的高可靠性0603M铜电极负温度系数NTC热敏电阻

株式会社村田制作所开发了0603M尺寸(0.60.30.3mm)铜电极负温度系数(NTC)热敏电阻&#xff0c;型号分别是“NCU03XH103F6SRL”和“NCU03XH103F60RL”&#xff0c;该新品扩充了NCU系列的产品尺寸阵容&#xff0c;满足了汽车市场应用中电路板的高密度化和小型化、以及对电子部件的…

AF路由模式组网部署

实验拓扑 防火墙基本配置 接口配置 eth1 eth2 eth3 路由配置 地址转换配置 放通策略 1. 出口申请了主电信、备联通两条外网线路&#xff08;均为 50M 带宽&#xff09;。 2. 内网有 web 服务器linux 172.16.3.100运行 http 服务&#xff0c;内外网用户通过 出口路由器…

Kubernetes 1.25 containerd 环境部署 SuperMap iManager

超图官网目提供的Kubernetes 版本为 1.20 版本&#xff0c;容器运行时为 docker 本次部署使用已有的 Kubernetes 1.25 版本集群&#xff0c;容器运行时为 containerd Kubernetes &#xff0c;containerd 部署请自行了解&#xff0c;本次不做介绍&#xff0c;下面介绍在此环境上…

MATLAB绘图基础5:MATLAB数据导入

参考书&#xff1a;《 M A T L A B {\rm MATLAB} MATLAB与学术图表绘制》(关东升)。 5.MATLAB数据导入 5.1 从CSV文件读取数据 C S V {\rm CSV} CSV文件是一种纯文本文件&#xff0c;文件中的数据以逗号为分隔符进行字段分隔&#xff0c;每一行数据代表一条记录&#xff0c;每…

通信工程学习:什么是AB地址总线、DB数据总线、CD控制总线

AB地址总线、DB数据总线、CD控制总线 在计算机体系结构中&#xff0c;总线&#xff08;Bus&#xff09;是一种用于在计算机内部各个组件之间传输信息的物理通道。其中&#xff0c;AB地址总线、DB数据总线和CD控制总线是计算机总线系统中非常重要的三个组成部分&#xff0c;它们…

机器学习-神经网络:循环神经网络(RNN)详解

引言 在当今人工智能(AI)和深度学习(DL)领域,循环神经网络(RNN)作为一种专门处理序列数据的模型,具有不可忽视的重要性。RNN 的设计目标是模拟和处理序列中的时间依赖关系,使其成为许多应用场景的理想选择,如自然语言处理(NLP)、时间序列预测和语音识别等。它不仅…

乐凡北斗车载终端 | 车载终端功能是什么?

北斗车载终端即北斗卫星监控系统主机&#xff0c;主要是通过北斗卫星实现定位和导航&#xff1b;并通过传输网络与监控中心通信&#xff0c;由前端设备、传输网络、监控中心构成北斗卫星定位监控系统&#xff0c;实现定位跟踪及智能功能, 终端还可内置8Mbit的Flash储存器&#…

分享一个基于uniapp科技馆服务微信小程序 博物馆管理小程序(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

Java stream使用与执行原理

stream简介 Stream: A sequence of elements supporting sequential and parallel aggregate operations stream为sequential即单线程串行操作&#xff0c;parallelStream支持并行操作&#xff0c;本文只讨论sequential的stream。 stream常用操作 Datastatic class Course {pr…

【软件工程】第二讲软件过程

【软件工程】第二讲软件过程 文章目录 【软件工程】第二讲软件过程1. 软件过程概述1.1 软件工程的金三角1.2 软件过程的定义1.3 软件过程的组成 2. 软件生命周期模型2.1 瀑布模型2.2 增量模型2.3 演化模型 3. 统一软件过程RUP3.1 RUP最佳实践3.2 统一软件过程RUP 4. 敏捷过程4.…

Qt-常用控件(2)-按钮类和显示类

​ 1. QPushButton 使用 QPushButton 表示一个按钮.这也是当前我们最熟悉的一个控件了 QPushButton 继承自 QAbstractButton.这个类是一个抽象类.是其他按钮的父类 在 Qt Designer中也能够看到这⾥的继承关系 QAbstractButton 中,和 QPushButton 相关性较⼤的属性 属性说明t…

触想全新Z系列工控机扩展IIoT应用潜能

8月31日&#xff0c;触想重磅推出全新Z系列高性能、扩展型工控机——TPC05/06/07-WIPC&#xff0c;提供标准版/双卡槽/四卡槽3款机型选择。 作为边缘计算、机器视觉、AI智能和工业应用的理想机型&#xff0c;Z系列工控机支持Intel第12/13/14代Core™ i3/i5/i7/i9处理器&#xf…

git如何灵活切换本地账号对应远程github的两个账号

git如何灵活切换本地账号对应远程github的两个账号 问题&#xff1a; 有时候我们会同时维护两个github的账号里面的仓库内容&#xff0c;这时候本地git需要频繁的切换ssh&#xff0c;以方便灵活的与两个账号的仓库可以通信。这篇日记将阐述我是怎么解决这个问题的。1. 第一个账…

kubernetes--配置与存储(ConfigMap、加密数据配置Secret、SubPath、热更新、Volumes、NFS挂载、PV与PVC)

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 出自B站博主教程笔记&#xff1a; 完整版Kubernetes&#xff08;K8S&#xff09;全套入门微服务实战项目&#xff0c;带你一站式深入掌握K8S核心能…

Tranformer分布式特辑

随着大模型的发展&#xff0c;如何进行分布式训练也成了每位开发者必备的技能。 单机训练 CPU OffloadingGradient Checkpointing 正向传播时&#xff0c;不存储当前节点的中间结果&#xff0c;在反向传播时重新计算&#xff0c;从而起到降低显存占用的作用 Low Precision Da…

HarmonyOS 是如何实现一次开发多端部署 -- HarmonyOS自学1

一次开发多端部署遇到的几个关键问题 为了实现“一多”的目标&#xff0c;需要解决如下三个基础问题&#xff1a; 问题1&#xff1a;页面如何适配 不同设备间的屏幕尺寸、色彩风格等存在差异&#xff0c;页面如何适配。 问题2&#xff1a;功能如何兼容 不同设备的系统能力…

身份证实名认证接口如何用C#实现

一、什么是身份证实名认证&#xff1f; 身份证实名认证又叫身份证实名核验、身份证二要素、身份实名核验、身份证验证&#xff0c;输入姓名、身份证号&#xff0c;校验此两项是否匹配&#xff0c;同时返回生日、性别、籍贯等信息&#xff0c;同时支持港澳台证件核验。 二、身…

【Three.js】实现护罩(防御罩、金钟罩、护盾)效果

前言&#xff1a; 在这篇博客中&#xff0c;我们将使用 Three.js 从零开始生成一个护罩效果。护罩将使用自定义的 Shader 材质&#xff0c;带有动画效果&#xff0c;最终呈现一个视觉上酷炫的护罩。此篇文章的重点将放在如何生成一个3D护罩&#xff0c;其它功能将在之后的博客…