负载均衡式在线oj项目开发文档2(个人项目)

judge模块的框架

完成了网页渲染的功能之后,就需要判断用户提交的代码是否是正确的,当用户点击提交之后,就会交给路由模块的/judge模块,然后这个路由模块就需要去调用jude模块了,也就是需要一个新的jude模块,这个功能也有control模块整合之后提供给上层。路由模块提供的判题路由服务会给juge模块提供一个字符串,这个json串中有题目的id,然后是用户的输入("因为我没有做相关的工作所以没有作用"),最后是用户的代码。这样的一个json串。

得到了用户提交的json串之后,将json串进行发序列化之后,就需要使用Judge去调用后台的compile_run服务器提供的编译允许服务了。但是对于用于提交的代码,只有函数,没有测试用例是无法运行的所以这里还需要对用户提交的代码进行拼接之后再提交给后端编译服务器去运行。

由此就知道了这个Judge模块需要做的工作:

对于这5个工作除了第三个,其它的在这个函数中都能够完成了。为了完成第三个工作,所以就需要完成负载均衡模块了。

在这个模块中也需要进行很多的差错处理。下面就来完成这个模块

负载均衡模块的编写

对于这个模块我首先需要创建一个新的文件夹和新的文件:

在这个文件中写入的就是现在存活的后端编译服务所在的ip地址和端口号,写文件的样式如下:

上图就是存在三台提供编译运行服务的后端服务器。以及自己对应的ip地址和端口号。

现在服务器的配置文件就已经有了。现在就需要在oj_control中设计负载均衡,首先既然是要负载均衡的选择服务器,所谓的选择也就是管理,如何管理呢?先描述再组织。所以需要先将后端的编译服务器使用一个结构体进行描述,然使用一个容器将这些结构体对象储存起来,这就是大体的设计逻辑了。下面是详细的代码:

但是因为某一个后端服务器可能会同时收到多个负载请求,让load不断++,并且这些请求是并行的,为了防止load出现错误,需要使用一个锁。这里使用c++中的锁。但是c++的锁无法进行拷贝,所以下面使用的是c++锁的指针。便于之后将描述服务器的对象放到容器中进行管理。

有了元素之后再写构造函数。

有了描述服务器的类,下面就是通过容器实现负载均衡了。

这个模块也就是LoadBlance,这里我选择的容器是vector.

那么如果这些机器不在线或者在线又如何表示呢?难道真的就是离线从这个列表中删除吗?这样有些麻烦了,这里再使用两个vector一个用于维护现在在线的机器,一台用于维护离线的机器。

也就是说如果将服务器的Machine填到list表中后,除非后来再增加新的主机否则这个vector就一直不在变了,如果出现了主机离线就直接使用下面的vector进行处理即可。

然后就是构造和析构函数了。添加完成之后,因为所有服务器的信息都在conf文件中,所以需要一个函数,用于读取conf文件中的内容,将现在在线的编译服务器形成对象放到vector中。

再创建一个全局变量:

最后再提供一个函数,用于在在线列表中选择负载最小的一台机器。

然后还要提供一个上线服务器的函数,以及一个下线服务器的函数

也就是对在线数组/下线数组的增删功能。

下面就来实现这些函数

首先是读取conf文件的函数也就是loadconf函数。

要读取文件自然需要先将文件打开了,

加载配置文件失败了,那么我的这个服务器也就只能提供题目和编写代码的功能了,judge功能就直接没有了,所以直接进行返回即可。

然后再配置文件中的文件信息的格式如下:

一行字符串中间使用:进行分隔,为了能够分别得到两个对我有用的信息,所以需要对字符串进行处理。这就需要使用之前写好的工具类了。

完成拆分之后,形成一个Machine对象,然后将值填入申请好一个锁,最后放到vector中。然后再将onliev的值进行更新。读取文件完毕之后,关闭文件。

下面是代码:

对于这个函数详细的细节请看上面的注释。

到这里所有主机的配置信息就全部都有了,并且每一个在线主机的id也都有了。然后将这个函数放到Load类的构造中,让其一创建对象就会完成读取文件的工作。

现在已经完成了读取配置文件的工作,下面就是完成负载均衡选择了,也就是smartchoice函数。

这个函数需要完成两个功能,第一个:使用选择好的主机(更新主机的负载),第二个:部分主机可能需要离线(当然这个离线和储存machine对象的vector是没有关系了,也就是要将某个主机的id从online移动到offline中)。

因为这两个功能,所以这个函数需要两个参数

注意这里的machine* m这个参数是存在问题的,之后会修改。

现在思考一个问题,未来可能会有多台客户端提交代码。也就是LoadBance服务可能会为多个执行流提供服务,为了保证这个服务的安全所以LoadBlance也需要加锁。

这样就能保证LoadBance访问的安全了。

现在LoadBance有了锁,所以smart服务就需要先去申请锁了。

这里我选择的是通过轮询的方式找到负载最低的主机。

上面的代码考虑了在在线主机为0的时候,表示已经无法提供服务直接返回false。然后就是在下面轮询寻找负载最低负载的代码也是存在问题的,因为一个执行流在轮询找最低负载的代码时,其它的执行流可能会让这个主机的负载进行增加或者减减。所以这里我在Machine中增加一个函数,用于安全的返回负载。

将这个函数放到上面的负载均衡代码中。

然后选择到了一个主机,就需要对对应的主机进行负载的更新了,所以依旧是要在machine中增加一个安全的增加负载的函数。同时完成任务的服务器也需要进行负载的减减。并且这两个函数也是会有多个执行流去访问的所以依旧需要使用锁去进行保护。

下面去完成这两个函数:

但是获取Load那个函数其实没有太大的意义只是为了统一代码。

现在这个模块就已经基本完成了,后面的代码暂时不做处理。先去完成judge模块。

现在主机有了,负载均衡也有了。就需要进行judge了。此时就需要将负载均衡模块放到ctrl中了

udge模块的编写

Judge模块后序是被server.cc模块调用起来的。

并且这个模块还会将题目的编号也传递过来。

这里我就修改一下judge函数的参数让其能够接收这个number。

既然题目的编号已经有了,那么传入的这个in_json的情况也就可以将题目编号删除了。

那么现在题目的编号已经有了第0步(上图少了一步)通过题目的编号获取题目。通过model模块这是可以做到的。并且这个返回值也不需要进行判断,因为这个judge模块是服务器返回给用户的,一定是存在的所以不需要进行判断。

然后用户提交的代码信息就在in_json这个json串中,下面要做的自然就是对这个json串进行反序列化,读取用户提交的信息了。

但是还有一个最重要的步骤就是要将用户提交的代码和测试代码进行合并形成一个新的代码。(之后还需要去编译模块设置一下g++,让其设置一个环境变量,很简单)

下面就是要形成一个新的json串了,这个json串用于提供给编译

服务。

此时进行后端请求的json串就有了。

之后就是选择负载最低的主机了。这个选择也是有策略的,你要请求几次呢?如果请求失败了呢?

首先关于选择策略,我的规则是一直选择,直到存在主机可用,否则所有主机都已经挂了。

下面就是选择主机的代码:

  这也是为什么在smartchoice函数那里我选择的是将二级指针作为我的参数,就是因为我打算在这里以指针的方式获取服务器的信息,所以在smartchoice中就只能使用二级指针的方式去储存machine的地址。

  下面就需要将编译允许模块发起请求了

  如何发起请求呢?在httplib这个第三库中是提供了对应的方法的:

  例如下面就是以post方式进行网络请求第一个参数你要请求的资源路径,第二个就是你要传递给对方的参数,第三个就是参数类型

  例如下面就是一个示例代码:

  只用将方法替换为post即可,函数如下:

  这里能够使用res去判断因为这个函数的返回值是一个

  Result,而这个Result:

  内部就是一个智能指针,既然是指针自然可以使用这种方法了,指针为nullptr,就肯定是失败的。

如果请求失败了,就打印一条日志:并且对这台服务器进行离线处理。

如果请求成功了,运行的结果就在compile服务发送过来的json串中,而这个串的内容就在res这个返回结构的body中。

之后如果请求成功那么自然是要让负载降低的。而如果这个服务器请求失败了那么负载也是需要降低的。选择如果成功了那么负载是要进行增加的。

但是其实当选择失败的时候,对负载的降低是没有必要的,因为在离线服务器的时候会直接将负载减少为0。

但是上面的代码还是存在问题的,首先就是即使是从编译服务端获得了响应,如果这个请求的状态码不是200,那么这个响应就是无效的。但是这里对于其它的状态码我这里不管,只处理状态码为200的响应。从这个响应的结构体中就可以看到状态码这个字段:

如果状态码为其它的就让这个主机的负载降低,然后去选择其它的主机。

代码如下:

如果连响应都没有说明这个主机已经挂了就需要去离线这一台主机,而我是知道这台主机的id值的,这个id就是离线主机的依据。

下面就是离线主机的函数编写。需要主机在一个执行流正在离线某一台主机的时候,如果不进行加锁,就可能导致另外一个执行流选择到这台离线的主机,所以这里需要进行加锁。

以下就是一个离线的功能了

对于让主机上线这个功能之后再说。

这里为了便于之后我的调试我增加了一个打印当前的在线主机/离线主机列表的函数。然后在请求主机失败的时候离线之后打印一下这个信息查看是否离线成功。

在编译运行服务成功的地方使用日志打印一下信息。

还剩下一个上线主机的功能,对于这个功能我现在设想的是当所有主机离线的时候再进行统一的上线。

之后如果有改变之后再说。这个machine就是一个描述编译服务的一个结构体。其中封装了一个负载量。而Load_balance模块就是管理machine的结构体。并且所有的主机在一开始的时候都是处于上线的状态的。

但是在重复选择的那里其实还存在一定错误,为了保证用户的使用,在没有选择到一台主机的时候会一直重复的进行主机的选择,但是现实是,如果存在某些主机挂了(不可能出现让某些问题主机一直被重复选择),那么运维人员会马上介入然后去进行处理。只不过我这里为了稳妥处理在选择主机的函数那里是一直会进行选择的。

但是现在在路由功能中还没有增加上Judge功能下面就进行增加

到这里judge模块除了上线模块以外都已经写好了。下面使用postman进行测试一下。

在进行测试之前还有最后一个工作就是将runner模块中的g++程序替换增加几个选项,让其能够由编译器生成一个宏。

在编译形成可执行程序那里进行这个操作:

下面再进行编译测试:

首先就是启动三个编译服务:

因为这里我只有一台主机,所以只能这样去进行测试了,未来这三个服务就可以部署在不同的服务器上。然后将上面这三个服务器的信息写到config文件中:

这样oj_server就能够知道这三台主机的信息了

并且服务器的列表也已经加载成功了。

下面就是使用post慢模拟发送请求给oj_server测试功能了

然后发送这个请求。

成功了

对于错误代码,也能给出回应

如果我离线了8080,就会出现下面的情况,自动选择还在进行服务的主机:

如果所有主机都离线呢?

因为postman发送请求的速度比较慢,无法直观的感受到负载均衡的进行选择。之后我在完成了前端之后会再进行测试。

到这里judge模块的测试也完成了。

到现在为止后端的核心逻辑已经写完了。现在只差前端了,下面就使用三个技术:html/css/js来完成这个前端,但是因为我对前端了解不深,所以写的不是很完备。

丐版首页

给index.html增加了导航栏:

但是实际网页没有变化。

要完成这个两个工作常见的方式有三种:第一种是内嵌式的属性写到标签属性中。还有一种是写一个css的文件,将所有的属性都写到这个css文件当中(和c++中声明放.h实现放.cpp中很像)还有一种就是将属性和第一种一样写到网页中,只不过不是写到标签上而是内联式的写到代码中。下图中的style标签中就是样式

然后在style中就可以进行标签式的选中了或者其它类的选中了。但是因为我不了解就不说明了。但是这个style就像c++中的hpp文件一样实现和声明都在一个文件中

在浏览器这里这些东西就已经加载过来了。

下面就是调整样式,首先需要内容整体居中。

整个网页就是一个大盒子,而在之前的代码中写的两个container就是大盒子中的两个小盒子,下面我需要要让这两个小盒子中的内容整体居中。如何做呢?使用.container去选中类(两个小盒子)

再去刷新一下网页。

颜色确实是变化了,下面就是设置整体居中了,如何设置呢?(content的)

上下边距为0,左右边距自动。这样就能让灰色的部分居中了。

但是文字我也想要居中(让文字在灰色的部分中居中)

但是文字挤在一起不好看。首先给文字也设置类名:

再去选择这个类名,设置字体style.

再去看网页

此时的网页就ok了。然后我还想将下划线去掉:

然后就可以让这个调试的背景颜色去掉了。

当然字体的大小和风格也是可以设置的:

风格我就不设置了

但是这样我感觉不太好看。就将这个字体大小取消了。

下面去设置导航栏

然后导航栏的样式我什么都没有实现

此时就有了。下面设置导航栏的样式

此时就有了一个黑色的导航栏。

但是这里的字有点挤。设置字体宽度即可。

再给这个导航栏一点动态效果:

此时就有了。再将这个文字设置为居中:

但是我觉得我的登录不好,我想放在另外一边。此时就需要一个浮动的概念了。

首先需要我将登录界面放到居右对齐。

然后单独设置这个登录的浮动效果:

但是这个浮动效果容易出现问题,所以需要进行设置:

最后我在将首页中的文字,居于网页中心。

但是这个首页的很多功能我并没有写。

到现在为止,丐版首页就完成了。

下面就是去完成题库的网页编写了。首先需要思考我的这个题目列表的整体构成是怎么样的。

第一部分是一个整体的容器。然后是导航栏,然后是题目列表最后是一个网页的footer这里不实现,但是可以带上一个。

题目列表由三个子盒子构成。

在question_list中放的就是我的题目列表:

然后将之前写的导航栏部分粘贴过来。

然后将和导航栏固定的style也粘贴过来。

之后只要去调整后序的样式即可。重启服务:

此时就存在导航栏了。

然后需要设置一下题目列表中的文字,让其居中表示

但是这个题目有一点不一样的是,这个题库是使用第三方库(cteplate)渲染过来的,这个渲染就可能还会保存之前的信息。所以要让其显示出效果最好就是重启一下服务。为了便于调试可以增加一个背景颜色。

可以看到这个居中效果还可以进行优化

现在所有的文字虽然都是居中的了,但是还能改进一下。表格元素还可以进行优化。首先就是将表格的大小将灰色区域填满。

然后调整一下文字的大小和样式

其中的font-family是文字的样式

最后将用来调试的灰色背景删除。

对于题目编号的打印问题,需要我之后优化一下后端进行一次排序对于这个问题之后再说。在进行一点优化:

让question_list这个容器和上面的这个容器空出50个色彩的边距。使用了内边距因为外边距会被下面的外边距再次设置导致失效。

但是这个标题有一点小,这里换成h1标签,然后在设置一下这个标签的属性(颜色修改为绿色)。

但是这个table中的文字和标题相距也有一些近了。

下面我想将表格中字体的样式进行调整。

之后只要使用them就可以批量化的进行调整了。

因为我没有艺术细胞,就不再继续搞了。

去优化一些其它的东西,首先我向给这个题目列表设置一个颜色谈的背景。

这样即可。然后将文字调整一下:

找到a标签

然后我还想给能够点击的a标签实现一个动态效果(鼠标动画)

当我选中的时候这个题目就变成蓝色了,并且会有下划线。

最后还有一个footer了

然后在运行

很明显这个字体的位置不合规则。这里我设置一个question_list这个容器的高度。就可以让这个测试内容放到后面了。

当我的题目内容不断增大的时候会不断增大这个内容。

然后就是footer的style设置了

高度宽度,文本居中和背景颜色的设置。

在弄一个上下居中:

然后将测试的背景颜色去除,在修改文字的颜色

但是现在我的题库中的题有点太少了,我增加一些题目。使用的是拷贝。

此时我就增加了一些新的题目。但是有一个小问题就是测试内容就和这个题目重合了。修改一下question_list的高度

到这里题目的信息都已经有了。现在题目的顺序是有问题的,后面再去排序解决。

之后就是最后一个界面的编写了。

单个题目界面的编写

当前我当个题目的编写页面如图,可以看到如果真要写代码,那么感受不用多说肯定是很差的。

并且如果题目的描述如果很多的话,题目描述的出现方式也不是很好,都是挤在一起的我的很多的\n都没有用了。

这里需要知道一个标签pre:

这个标签能够尽量保全我文本的原貌。

添加这个标签之后,重启服务。

此时题目就按照原貌显示出来了。之后就是字体的优化了。

下面首先依旧是增加导航栏。

这里首先使用一个ACE插件,用于完成前端的在线代码编辑器。

这个插件的使用对于我来说有点麻烦了。所以这里我就直接复制了一份代码。虽然有了ACE的代码,但是要将这个代码使用到我的前端页面上也有步骤需要做。

首先就是引入ACE CDN

然后是增加我的代码。和窗口

下面这些就是对这个在线编辑窗口的属性进行调整的代码

在网站中就有了这样的一个代码编辑器,如果你将上图中的false。改为true,那么用户就无法写代码

并且将我之前写的预设代码也已经写到了这里面。

下面就是我的这个网页的整体框架了:

其中的left_desc就是题目描述,right_code就是上面说的这个ACE在线代码编辑器。最下面的就是一个按钮用于提交代码到后端的服务器以及结果的查看。

到这里容器已经有了下面要做的就是样式调整。

但是现在没有一个大的容器,所以下面首先使用一个大的容器将这些储存起来

这个网络页面的结构就是第一部分导航栏,第二部分显示左说明的题目信息,和右边的在线oj代码(用户提交)最后一部分就是提交按钮。但是实际上这个botton中还要有一个东西,用于显示oj的结果。

对于导航栏还是整体复制下来即可。

但是上面的代码在转化为网页之后题目描述和代码编写页面依旧不是左右页面。这里就需要做style编写了

这样之后就可以在左侧读题目,右侧写代码了。

但是现在我还想要让结果显示在左侧,让提交按钮显示在右侧。

依旧需要做样式调整

下面需要调整一下细节性的东西。首先就是既然后面的标签使用了float那么需要前面的父类容器增加hidden

下面再去调整一下题目的字体,以及字体间的间距

这样做了以后出现了一个问题:

题面的描述被代码编辑器给遮盖了,并且如果你在后端增加题目描述的话还会出现下面的问题:

力扣的页面则是存在上下滑动的,甚至于左右滑动都是有可能的。要让我的界面也呈现这样的效果,就需要给left_desc增加属性了

这条scroll就是添加滚动条,设置滚动条需要指定高度。

此时滚动条就出现了。

后面我又将字体大小设置为了medium。到这里左边就已经完成了。对于代码如果存在很多的情况是不需要担心的,因为ACE在线编译器自带有滚动条的。

下面再设置一下这个ACE编译器的窗口大小。

需要注意必须设置到这里直接设置到.right_code这里是不可行的

这里我在设计一下提交按钮的style

这里最后没有带上圆角因为我的美术能力实在驾驭不了。

但是这个按钮和上端挨着很近,并且和左边挨着也比较近了。

再修改一下:

到这里界面就已经完全完成了,下面要做的就是前后端交互。也就是在ACE编辑器中的代码再点击了提交之后要提交给后端对应的服务。

首先我先给按钮添加一个事件

在点击了这个提交之后会直接去执行submit这个函数。这个函数是一个js函数。

这里先测试一下:

然后是当我的鼠标在这个提交按钮的时候我希望我的鼠标也能出现一些变化。

点击后:

但是这些东西都是前端,我现在需要的是和后端进行交互。

在submit函数这里,就需要收集有关数据主要是题号,代码,input(不用管)。

完成下面的步骤也就完成了前后端交互的功能。

下面就是前后端如何进行交互了。

也就是上面三个js函数的编写。

这里使用JQuery来获取html表单中的内容。

下面就是引入jquery CDN

这里先测试一下:

使用上面的步骤获得的信息就直接打印在控制台中了

下面首先就是获取代码在ACE编辑器中本来就有获取用户输入的文本信息的接口。

然后测试一下:

这样就拿到了代码,然后是这个代码对应的题号,这个题号就在number(标题)中,这里就可以给number加一个span标签。

然后继续去完成这个sunmit函数

测试截图:

获得了代码和题号。

然后就是发起请求了。

到这里就能够完成字符串拼接了。

下面就是创建json串去发起请求了。这里就完成了收集页面的内容。这里是通过ajax去向后台发起请求。

这里当发送了请求之后会发送报文到后端的judge服务,这里我可以DEbug测试一下

这里测试一下是否能够得到结果

可以看到获得了用户提交的代码,也获得了后端提交的响应。

即使我提交了一个超时的代码,或者错误的代码

对于status 11 号,也有可能是其它的问题。之后可以修改。

下面直接进行测试:

这样就完成了将数据从前端到到后端的交互,也就完成了发起数据请求的功能。

最后就是将结果显示到网页中了。

现在后端发送的信息已经在data中了下面就是完成将data中的信息显示到网页上了。

依旧是完成一个show_result函数

上面注释的两行都是我用来进行测试的。然后获取status和reason:

如果status = 0代表编译没有问题,但是测试用例是否通过。

当status不等于0的时候说明编译运行错误了。但是我的show_reason函数无论你成功还是失败都会返回原因所以这里的else就什么都不做了

然后调整一下result的style:

这样就能够显示出来了。在测试一下错误代码

并且因为每一次我都会clear我的result_div所以这里不会存在上一次的信息。

至于其它的错误,我这里就不再测试了。

到这里显示的代码就完成了。

到这里前后端已经搞定了,先面就是综合调试以及题目的顺序问题。

负载均衡测试和题目乱序解决

首先解决题目编号的乱序问题,这些题目都是放在

然后再control中进行获取到了一个vector中,但是我这里不打算对这个questions进行排序,现在我想要的就只是展示了给用户的界面要有序即可。所以sort下面的这个数组一样也是可以的

运行测试一下。

下面测试一下负载均衡是否能够正常的运行。为了进行测试我这里在增加一条信息也就是会打印主机的负载信息。

之后我不断的按提交看是否会负载均衡的选择

可以看到当我不断的提交的时候确实会负载均衡的帮助我进行选择后台服务器。

离线一台:

 

当只剩下一台的时候可以看到明显负载大了很多:

当全部离线:

但是这里没有上线函数,首先上线的前提是所有的主机已经全部下线了,现在上线是将offline数组中的元素,全部放到online中,即可完成上线,只有所有后台服务器都下线了才需要进行上线。

再之后当运维的人员将后端服务器重新上线之后我这里通过信号的方式重新上线所有服务器,同时当上线的时候,也不能让之前下线时服务器的数据,再影响重新上线的数据,所以需要先修改一下下线函数。也就是要提供一个新的函数让其能够让某一个后端主机的负载清空为0,然后再下线主机之前调用这个函数。

然后在ctrl模块中增加一个恢复主机的函数

之后再oj_server.cc中增加一个信号捕捉:

然后测试:

然后我关闭所有的后台服务:

然后我重新启动所有后台服务:

服务重新启动成功,只不过这个重新启动没有信息打印增加一下信息打印,即使我是只有一台主机下线,重新上线之后也能使用ctrl+c重新上线。

可以看到ctrl+c之后这个8890又出现了。然后我给这个信号再增加一点打印即可

这样即可。下面要做的就是将题库中的题目由文件版本修改为数据库版本

修改题目列表的形式为数据库形式

首先要将题目列表从文件版本修改为数据库版本,首先就需要一个数据库用户,这个用户能够进行远程的连接,这里我就先创建一个这样的用户,并且还需要一个数据库专门用于储存题目的信息。

首先是创建数据库:

这个数据库是由数据库的root用户创建的,之后会赋权给能够进行远程连接的用户。

然后是创建能够进行远程连接的用户,这里涉及到密码操作我就不截图了,但是因为这里我打算在我的windows机器也能够使用这个用户连接到数据库,所以我设置的这个用户能够连接的IP地址是全部IP都可进行连接,在正式的开发中这是不允许的,也是不安全的,这相当于将我的MySQL端口暴露到了公网中,这是很危险的。但是我这里只能这么做。

创建好用户和数据库之后需要将这个数据库赋权给这个新用户:

然后我Windows上通过mysql Workbench使用这个账号进行一次远程登录。这里就不显示了,但是这个账号确实是可以正常进行登录的。

下面要做的就是建立合适的表了,

设计合适的表结构

现在就要根据我的题目来设计合适的表结构了,这里我使用Navicat去远程链接我的数据库,来完成建表的工作。

下面就是思考这个表中具有哪些属性列了,首先题目的编号肯定是要具有的(number,类型int,long都是可以的),下一个属性列就是题目的标题(tittle,字符串)了,标题之后就是这个题目的难度了,下一个题目的难度(star字符串),下一个就是题目的描述,题目的描述一般都是长文本(text类型),然后就是预设给用户的代码,自然使用的也是长文本(text),然后是测试用例,依旧是长文本(text),之后就是题目的时间限制(int)和题目的空间限制了(int)已上面的属性列来进行表的创建。

这样就创建出来了我想要的表:

向表中插入数据

也就是往这个表中插入一个数据,用于测试,也就是将求最大值这道题目录入到这个表中

这里我就将第一道题目录入到了这个表中

在xshell中进行查询也成功完成查询了。

所以在数据库中使用图形化界面就不需要写录题的代码了,使用图形化界面是可以轻松完成的。

修改代码为第二版数据库版题目

现在在数据库的表中已经具有了我需要的代码,现在就需要使用代码去连接这个数据库,链接数据库这里我介绍两种方法:第一种使用MySQL的开发包,第二种:使用MySQL的第三方库。这里我使用第二种方法。

这种方法需要下载一个第三方库:

mysql-connector-c-6.1.11-linux-glibc2.12-x86_64.tar.gz名字为这个,可以在MySQL的官方网站上进行下载。

下载完成之后放到引入到服务器中的第三方库的文件夹中,进行解压。解压后的文件夹如下:

然后需要建立include和bin文件夹的软连接到项目的oj_server开发目录下。

建立了软连接。

再写代码之前,需要知道的是oj_server这个模块是按照MVC架构去进行的设计,现在将题目进行了更改,也就是对数据进行了更改,也就是说现在现在修改了题目的数据版本之后只需要修改oj_server中的oj_model模块即可。这里我重命名一下这个model模块,将其修改为MySQL版本,将两者的接口设计一样,这样写的好处就是其他模块不需要大的改变。对于这个模块因为我不需要从文件中获取题目而只需要从数据库中获取数据,所以下面几个函数是可以删除了:

LoadQuestionlist//在文件版中这个函数用于读取文件版本中的信息到unordered_map,但是现在已经不需要了。既然是从MYSQL中直接获取数据,所以unordered_map这个成员变量自然也就不需要了。

现在这个模块只需要完成两个函数:第一个获取全部题目的函数,第二个获取单个题目的函数。

第一个函数也就是将获取到的题目放到一个vector中,第二个则是将获取的题目信息也放到vector中:

之后从vector中获取第一个数据即可。

到这里获取题目信息的上层逻辑就完成了。下面要做的事情就是完成Query_Mysql函数了。也就是需要完成Query_Mysql这个函数了,这个函数的作用就是链接数据库。

首先要链接数据库需要将下面的这些变量创建好:

然后创建MySQL句柄:

然后需要使用下面的这个函数来完成对MySQL数据库的链接:

下面要做的也就是执行这个sql指令了,使用的函数为mysql_query,这个函数执行成功返回的就是0

下面要做的就是将这一次查询出来的结果以字符串的形式解析出来了。下面要将查询出来的结果,放到输出型参数out中

但是到这里还有一个小细节没有处理,那就是result这个结果集中的数据还没有被释放掉,这个结构体因为内部储存有这一次查询出来的结果,所以最后要释放掉,最后去关闭MySQL数据库的链接。

到这里这个函数就写完了。到这里model2模块就完成了。下面要做的就是将这个模块放到其他模块中去。然后需要修改oj_view模块将其变成从oj_model2中获取数据。下面即使调式这个代码了,到这里这个项目的所有的代码部分就完成了。最后需要修改一下makefile文件因为这里我们使用了第三方库,而这个第三方库是需要在编译的时候告诉g++的,所以最后需要加上一句-lmysqlclient

完成上面几步之后就可以完成编译了,当然如果你的系统中已经安装了MySQL的开发包,那么之前做的两个软连接是可以不用创建的。

例如我上面编译的这个程序因为我已经在默认路径下安装了MySQL的开发包,所以ldd显示的开发包中MySQL的开发包使用的是我默认安装路径下的开发包,这里建立了软连接只是为了适应更多的场景而已。如果你已经安装了这个软件包,但是还是显示这个第三方库的依赖为空,那么可以去下面这个文件路径下创建一个.conf文件这个文件内部就写MYSQL这个开发包的路径即可:

是否可行呢?启动oj_server之后访问一下网站即可。默认的网站页面已经出现了:

点击题目列表:

再去点击单个的题目:

都是能够获取成功的,但是这里还有一些小细节需要去处理,虽然我这里显示的中文是没有问题的,那是因为没有出现字符格式不一致的情况,但是在某些时候会运行出下面的结果:

也就是页面乱码了,这个时候就需要去考虑链接的编码是否不正确。此时就需要在代码链接数据库成功之后去调整链接的编码,这个步骤无论是否出现乱码问题都需要去写。

如果想要更多的题目通过数据库录入即可

完成了这一步这个项目就基本完成了。还剩下一个顶层make file文件的编写。

项目亮点

这个项目的扩展部分: 1.基于注册和登录的录题功能

2.业务扩展,自己写一个论坛,接入到在线OJ中

3.即便是编译服务在其他机器上,也其实是不太安全的,可以将编译服务部署到docker上

4.目前后端编译服务,使用的是http方式请求(仅仅是因为简单),但是也可以将编译服务设计成为远程过程调用(推荐第三方库:rest_rpc库),替换httplib(建议可以不做)

5.功能上更完善一下,判断一道题目正确之后自动下一道

6.将所有的功能都实现。

顶层makefile确保项目的发布和清理

分析一下我的这个项目需要的两个大模块一个是编译运行服务(compile_server),一个就是oj_server服务,这两个模块只需要生成两个可执行文件即可所以顶层的makefile文件就可以这么写:

运行结果:

但是这样还有一点小瑕疵就是每一次make都会将makefile中的运行过程都显示出来,我并不希望这样,此时就可以修改一下makefile文件

修改后的makefile:

新的执行结果:

这样就能够在不显示执行过程的情况下将生成两个可执行程序了。但是到这里还没有完成发布功能,在别人想用我的项目的时候,我不可能直接将代码+可执行程序一起发送过去的,所以这里还需要创建一个output文件夹,里面保存的就是可执行程序以及可执行程序所依赖的各种包。

最后清理的时候还需要删除这个output这个发布包:

下面再整体使用一下这个makefile文件:

然后会生成一个output文件夹:

这个文件夹中具有的就是这个项目要执行的两个可执行程序以及依赖文件,到这里这个项目就暂时完结了,之后写扩展功能会再更新。

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

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

相关文章

setContentView调用流程(二) -将布局添加到mContentParent

Android setContentView执行流程(一)-生成DecorView Android setContentView执行流程(二)-将布局添加到mContentParent 上篇博客我们介绍了setContentView的第一步即生成DecorView以及获取到mContentParent的流程,同时还提到继承自Activity和AppCompatActivity生成…

【C#设计模式(2)——工厂模式】

前言 工厂模式:使用工厂创建对象。工厂模式的主要目的是分离对象的创建与调用,通过使用工厂统一管理对象的创建。工厂模式可以隐藏对象的创建细节,使客户终端代码只关注使用对象而不需要关注对象的创建过程。 运行结果 代码 #region 食品 /…

Dockerfile

1. Dockerfile 简介 1.1 什么是Dockerfile Dockerfile是一个用于定义和构建Docker镜像的文本文件,它通过一系列指令和参数来描述镜像的构建过程和配置。这些指令包括基础镜像、软件包安装、文件拷贝、环境变量设置等,使得应用程序及其依赖项可以被打包…

VBA高级应用30例应用3在Excel中的ListObject对象:插入行和列

《VBA高级应用30例》(版权10178985),是我推出的第十套教程,教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开,这套教程案例与理论结合,紧贴“实战”,并做“战术总结”,以…

C++OJ_二叉树的层序遍历

✨✨ 欢迎大家来到小伞的大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C_OJ 小伞的主页:xiaosan_blog 二叉树的层序遍历 102. 二叉树的层序遍历 - 力扣(LeetCode&#xff0…

ctfshow-web入门-反序列化(web265-web270)

目录 1、web265 2、web266 3、web267 4、web268 5、web269 6、web270 1、web265 很简单的一个判断,满足 $this->token$this->password; 即可 由于 $ctfshow->tokenmd5(mt_rand()) 会将 token 随机为一个 md5 值,我们使用 & 绕一下&am…

【STL】queue,stack的底层实现

在前面的介绍中我们已经知道了queue和stack是一个容器适配器,它并没有被划分到容器的行列,它只是对其他容器的再封装,在STL中queue和stack默认使用的容器是deque 在数据结构的学习中,我们知道stack和queue可以使用顺序表和链表实现…

Tomcat安装和配置(超详细)

一、Tomcat安装准备 1、tomcat下载 1.1、百度网盘链接下载 链接:https://pan.baidu.com/s/1uceOKe_QcpSQ6yhNxi4T5g?pwd1234 提取码:1234 1.2、官网在线下载 Tomcat官网:https://tomcat.apache.org/download-80.cg…

Ozone调试WSL系统的STM32编译文件配置

文章目录 背景步骤Ozone新建工程流程配置Ozeon找到WSL的代码文件ozone字体调整快速在Ozone中定位到代码文件参考 背景 在使用WSL进行嵌入式软件开发的时候,在debug方面,比较好用的工具有Ozone,那在Windows下调试需要配置和注意的点&#xff…

洛谷 P2239 [NOIP2014 普及组] 螺旋矩阵

本文由Jzwalliser原创,发布在CSDN平台上,遵循CC 4.0 BY-SA协议。 因此,若需转载/引用本文,请注明作者并附原文链接,且禁止删除/修改本段文字。 违者必究,谢谢配合。 个人主页:blog.csdn.net/jzw…

python通过usb连接标签打印机-开源的

背景: 最近接到了一个新需求,单位想做一个ERP系统,想把打印机一起兼容进去,实现自动化打印工作。主要我是做爬虫的没接触过这些,就到网上搜索了很多先关资料,最终发现,一大堆全都是什么VIP的才能…

Codeforces Round 984 (Div. 3)

题目链接 A. Quintomania 题意 思路 模拟即可 示例代码 void solve() {int n;cin >> n;vector<int>arr(n);fer(i, 0 ,n) cin >> arr[i];fer(i, 1, n){if(abs(arr[i] - arr[i - 1]) ! 5 && abs(arr[i] - arr[i - 1]) ! 7){cout << "N…

【2】GD32H7xx 串口Idle + DMA接收不定长数据

目录 1. IDLE中断相关介绍2. D-Cache与DMA同时使用2.1 I-Cache与D-Cache2.2 D-Cache与DMA同时使用时的数据一致性问题2.2.1 CPU读取DMA写入到SRAM的数据2.2.2 DMA读取CPU写入到SRAM的数据 3. Uart Idle DMA收发程序4. 程序测试 1. IDLE中断相关介绍 在 GD32H7xx MCU 中&#…

python数据结构基础(8)

今天来使用python实现二叉树,二叉树中每个节点都是Node类对象,通过Tree类中的add()方法逐个向二叉树中加入树节点,构成完全二叉树或者非完全二叉树,代码如下: class Node(object):"""树节点类&#xff0c;用于构建二叉树。Attributes:- val: 节点存储的值。- r…

IEEE 1588:电信网络的精确时间协议 (PTP)

IEEE 1588&#xff1a;电信网络的精确时间协议 IEEE 1588 PTP 概述PTP 协议特征同步类型IEEE 1588 PTP 角色IEEE 1588 PTP 的工作原理PTP 设备类型PTP 消息类型事件消息一般信息 PTP 时钟类规范PTP 配置文件 https://www.techplayon.com/ieee-1588-precision-time-protocol-ptp…

深度学习基础—了解词嵌入

引言 上图是使用one-hot向量表示词向量的一种方式&#xff0c;这种表示方式优点是方面简洁&#xff0c;但是缺点也很明显&#xff0c;就是词与词之间独立性太强&#xff0c;没有关联&#xff0c;这样使得算法对相关词的泛化能力不强。 举个例子&#xff0c;假如我们已经学习到了…

实战:索引的命中机制

在 SQL Server 中,查询是否能命中索引(即是否能使用 Index Seek)取决于多个因素,包括索引的结构、查询条件的排列、和数据库优化器的策略。以下是一些常见的命中索引和不能命中索引的情况,及其详细解释: 一、命中索引的情况 1. 前导列匹配(典型的命中索引场景) 索引结…

Mac 安装protobuf2.5.0

文章目录 一、修改platform_macros.h二、编译protobuf三、配置环境变量四、测试 一、修改platform_macros.h platform_macros.h的目录位置为/Users/xxxx/protobuf-2.5.0/src/google/protobuf/stubs 在platform_macros.h中增加如下代码 #elif defined(__arm64__) #define GOOG…

ubuntu24.04安装matlab失败

又是摸鱼摆烂的一天&#xff0c;好难过&#xff5e; 官方教程&#xff1a;https://ww2.mathworks.cn/help/install/ug/install-products-with-internet-connection.html 问题描述&#xff1a;https://ww2.mathworks.cn/matlabcentral/answers/2158925-cannot-install-matlab-r2…

python使用turtle画图快速入门,轻松完成作业练习

turtle介绍 turtle是一个绘图库&#xff0c;可以通过编程进行绘图。其模拟了一个乌龟在屏幕上的运动过程。该库通常用于给青少年学习编程&#xff0c;当然&#xff0c;也可以使用其进行作图。 在一些学校中&#xff0c;可能在python学习的课程中&#xff0c;要求完成turtle绘…