【负载均衡式在线OJ】实现编译,运行,测试等功能

项目主要内容:1.在线OJ;2.负载均衡

三大模块:

1.公共模块comm

提供字符串操作,文件处理,网络请求等

2.编译运行模块OJ_server

聚焦在服务器当中,当用户将代码提交上来之后,把用户提交的代码在我们的服务器上面形成临时文件,并且进行编译和运行,得到运行结果;

3.OnlineJudge模块

采用MVC的设计模式,让我们能够调用后端的编译模块以及能够访问文件或者数据库,把我们的题目列表和编辑界面展示给用户,让用户能够正常操作

编译好之后会形成两个可执行程序:1.编译服务器;2.在线OJ服务器;这两个服务器之间采用网络套接字的方式实现互相通信,这样就可以将编译模块部署在服务器后端的多台机器上,而我们的OJ_server只有一台,这样我们的服务器OJ_server会负载均衡地去选择后端的编译服务,让我们能够以集群处理能力的方式去对外输出我们的在线OJ服务,所以我们这是一个完全可扩展的项目

演示

1.功能1:在线OJ:

这个在线oj写题的模块是在前端里面用了一个20行代码不到的小插件实现的
在这里插入图片描述

2.功能2:负载均衡

在这里插入图片描述
重点完成:1.完成核心的题目列表之后可以自动录题,录题功能不作为重点(涉及到权限管理,各种前端页面)2.在线OJ,能够处理各种报错,后端采用文件+数据库或者数据库,3。实现基本的负载均衡
(完成的是力扣/牛客中的一个子功能)

所用技术

在这里插入图片描述
在这里插入图片描述

开始实现

02项目准备工作

1.在这里插入图片描述
2.在vscode中实现
在这里插入图片描述
在这里插入图片描述
当用户提交代码后,我们要负载均衡的选择某一台主机(compile_server)
CS模式:客户/服务器
BS模式:浏览器/服务器模式

03编译功能开发

在这里插入图片描述
在这里插入图片描述

compile_server编译服务最终要以网络的形式对外提供一个叫做编译和运行的一个网络服务
在这里插入图片描述

  • 该编译文件需要创建3个文件名

在这里插入图片描述

  • 当传进来的文件没有后缀的时候,需要自动为其生成后缀(在comm中新建util.hpp)
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述
如果编译成功,文件会存入
在这里插入图片描述
如果编译不成功呢?会返回false,但是编译出错的原因是什么呢?
一旦编译出错,g++会打印一个出错信息,如果想要得到这个出错信息
在这里插入图片描述
因此,要在子进程替换之前打开标准错误文件,再完成重定向的功能,如果编译出错,把编译出错的信息写在标准错误文件之中,
在这里插入图片描述在这里插入图片描述

04日志功能开发

在这里插入图片描述
里面会使用到Timeutil函数,需要自己在util中定义
在这里插入图片描述

  • 定义宏
    在这里插入图片描述

05调用日志开发

  • List item

获取时间戳
在这里插入图片描述

  • 获取时间戳(_time.tv_sec 获取时间的秒数)
    在这里插入图片描述
  • 添加日志

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

06测试编译模块

上章完成了编译模块,现在需要对编译模块进行测试,

在compile_server.cc中调用compiler.hpp的Compile函数
在这里插入图片描述
在这里插入图片描述
但是文件名是怎么来的呢?应该在compile_server的temp文件下有所需要的文件
在这里插入图片描述
在这里插入图片描述
运行之后,发现编译失败
在这里插入图片描述
问腿所在:
在这里插入图片描述
在这里插入图片描述
改正之后,编译成功
在这里插入图片描述

07运行功能模块

在这里插入图片描述
运行的时候必须在子进程里面运行,不能让主进程(父进程)执行,因为在父进程里要发生替换的话会把编译模块全替换了
在这里插入图片描述
在这里插入图片描述
一旦报错会形成一个同名的compile报错
在这里插入图片描述
在这里插入图片描述
如果我们想要把标准输入,标准输出,标准错误也存储起来:
在这里插入图片描述
那么怎么得到标准输入呢?
在这里插入图片描述
在这里插入图片描述
打开文件
在这里插入图片描述
如果三个文件中有任何一个文件打开失败了,就返回打开文件失败
在这里插入图片描述
子进程默认的标准输入,输出,错误本来是键盘显示器显示器,现在要把他们重定向成那三个打开文件
在这里插入图片描述
替换
在这里插入图片描述
判断程序运行是否异常
在这里插入图片描述
为什么要将run模块的返回值定义为int
在这里插入图片描述

08测试运行模块

在这里插入图片描述
编译运行
在这里插入图片描述
运行成功,然后添加日志
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果想重新编译测试,可以把之前生成的临时文件删掉
测试:如果运行有错误
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
接下来是要对runner模块的第二个功能的扩展:

09.资源限制初识

1.CPU使用超时:如果有人恶意攻击,占用cpu资源,那么就要采用一定的手段终止
在这里插入图片描述
2.内存申请失败:如果占用内存过多,会不断的申请空间,需要进行约束
在这里插入图片描述
在这里插入图片描述
限制之后
在这里插入图片描述
资源不足,导致OS终止进程,是通过信号终止的
在这里插入图片描述
运行之后,可以看到返回的信号,以此来确定问题出在了那里
在这里插入图片描述

10.给runner设置资源限制

在这里插入图片描述
在这里插入图片描述
提供设置进程占用资源大小的接口
在这里插入图片描述

11. 认识jsoncpp

第三个功能:compile_run功能:编译并运行功能
(适配用户请求,需要定制通信协议字段)
(正确的调用compile and run)
(形成唯一文件名)
在这里插入图片描述
修改
在这里插入图片描述
修改后
在这里插入图片描述

安装json
在这里插入图片描述
在这里插入图片描述
直接编译会报错,因为不认识上面那些东西
在这里插入图片描述
如何解决呢?------引入-ljsoncpp这个库
在这里插入图片描述
在这里插入图片描述
还可以换一种格式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
下面就着手编辑Start

12.编写cr模块1

上节课的序列化:将多个kv值转化成一个字符串
在这里插入图片描述
本节课进行反序列化:将一个字符串解析成多个kv值
在这里插入图片描述
在这里插入图片描述
已经将值进行了反序列化,反序列化之后我怎么拿到用户的标准输入代码和数呢?
提取用户的代码和input
在这里插入图片描述
如果用户提交的代码为空
在这里插入图片描述

如果不为空:
形成一个唯一的路径名,并且把code内容写入到temp文件下
在这里插入图片描述
接下来需要写一个形成唯一路径名的方法UniqFileName
在这里插入图片描述
在这里插入图片描述
形成唯一的文件名之后,就需要把code内容写进文件之中
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
如何解决后缀问题?可以用之前写过的方法:可以在指定文件下生成带指定后缀的函数名
在这里插入图片描述
在这里插入图片描述
输入接口:加入时间要求,空间要求
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
现在只处理了in_json,out_json还未处理,还有一些差错问题未处理
在这里插入图片描述
在这里插入图片描述

13.编写cr模块2

必填:状态码:0/1/2等等由客户端判定
原因:这次出错的原因(如果是编译时出错,为什么出错;如果是运行时出错,为什么出错)
选填:stdout:如果运行成功,结果是什么
stderr:如果运行出问题,输出又是什么
在这里插入图片描述
处理差错问题
在这里插入图片描述
下面这段代码处理前
在这里插入图片描述
处理后:如果生成临时src文件失败,则输出(我们自己知道是服务器问题,但不能跟用户说)
在这里插入图片描述
完成上面步骤后,说明我们拿到了想要的值,对应的代码不为空,并且代码也成功写入到文件当中,接下来就是编译
接下来对编译是否成功进行判断,修改前:
在这里插入图片描述
修改后:
如果编译失败,需要知道失败原因,所以需要读取之前生成的错误文件,,因此需要写一个读取错误文件的函数
在这里插入图片描述
在这里插入图片描述
判断运行是否错误
在这里插入图片描述
在这里插入图片描述
按照以上的写法,每个函数都要写对应的错误,太麻烦了。可以把错误汇总在一起status_code,然后在每个函数里调用对应的错误信号
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
reson那里怎么处理呢,首先换一下函数名,修改前:
在这里插入图片描述
修改后:
在这里插入图片描述
在这里插入图片描述
如果运行成功,则我们需要运行结果,运行结果全部被重定向到了Stdout和Stderr中,所以我们需要从Stdout和Stderr中读取
在这里插入图片描述
在这里插入图片描述
接下来应该做序列化,和之前一样,调用writer方法,形成字符串,然后输出即可
在这里插入图片描述
以上实现了全部的结构模块,接下来就需要补充里面未完成的模块

14.编写cr模块3

在这里插入图片描述
在这里插入图片描述

如果发生编译时的错误,我们还想知道编译时报错的文件的内容
在这里插入图片描述
所以:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

15.编写cr模块4

获得毫秒时间戳+原子性递增唯一值来保证唯一性
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上实现了唯一文件名

16.编写cr模块5

完成写入文件的功能
在这里插入图片描述
在这里插入图片描述
读文件
在这里插入图片描述
读文件的时候,有时候需要保留\n,但是getline的时候不会读取\n。

在这里插入图片描述
接下来在运行文件里调用该函数
修改前:
在这里插入图片描述

修改后:
在这里插入图片描述

17.解决cr模块语法报错

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

修改之后跑起来了,接下来需要设计测试用例
在这里插入图片描述

18.综合调试cr模块

先写一个简单的测试
在这里插入图片描述
在这里插入图片描述
这是C++11俩面的新特性,可以保证字符串中的特殊标点符号不被改变
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
运行之后,错误码为-3,则是编译时报错
在这里插入图片描述
在这里插入图片描述
修改后:
在这里插入图片描述
编译成功
在这里插入图片描述
生成的临时文件
在这里插入图片描述
测试1:如果输入是死循环
在这里插入图片描述
编译成功,但是错误码为24
在这里插入图片描述
测试2:只能申请10M,但我要申请20M
在这里插入图片描述
共享空间太小了,只有10M,可能连库都加载不进来,所以修改一下测试用例
在这里插入图片描述
修改之后,只能申请30M,但我要申请50M
在这里插入图片描述
编译成功,但是错误码为6
在这里插入图片描述
测试3:浮点数错误
在这里插入图片描述测试4:
在这里插入图片描述

19.清理临时文件


要清楚某个文件,就要先确定这个文件是否存在,之前写过判断文件是否存在的函数,直接用
在这里插入图片描述
删掉命令:unlink
在这里插入图片描述
因此,当文件存在时,将其删掉
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_47952981/article/details/129726465
今日推荐