赞
踩
该模块功能:
MVC结构:
这是和数据交互的模块,对外提供访问数据的接口;
题目的信息包括:
两批文件构成
desc.txt
),题目的预设置代码(header.cpp
), 测试用例代码(tail.cpp
)-D COMPILER_ONLINE
:这两个内容是通过题目的编号,产生关联的
header.cpp
拼接上题号对应的测试用例tail.cpp形成新的源代码,并发送到compile_and_run模块运行,运行结果返回给用户g++ -D COMPILER_ONLINE
)Model
类中,使用哈希表保存题号与题目信息的映射;
LoadQuestionList
函数用于加载所有的题目信息道内存中,以哈希表的形式;GetAllQuestions
用于获取所有的题目信息;GetOneQuestion
用于获取指定题目信息;mysql> use mysql
mysql> create user oj_client@'%' identified by 'password';
mysql> create database oj;
mysql> grant all on oj.* to oj_client@'%';
登录oj_client用户,可以看到oj数据库
create table if not exists `oj_questions` (
`number` int primary key auto_increment comment '题目的编号',
`title` varchar(128) not null comment '题目的标题',
`star` varchar(8) not null comment '题目的难度',
`desc` text not null comment '题目的描述',
`header` text not null comment '题目预设给用户的代码',
`tail` text not null comment '题目的测试用例代码',
`cpu_limit` int default 1 comment '题目的cpu运行时间限制',
`mem_limit` int default 50000 comment '题目的内存空间限制'
)engine=InnoDB default charset=utf8;
yum install -y mysql-community-devel
安装好devel(开发库)后,我们只需要用 #include <mysql/mysql.h>
就可引入mysql库。
编译指令为:
g++ -o oj_server oj_server.cc -std=c++11 -L/usr/lib64/mysql/ -lmysqlclient
oj_server是基于MVC实现的,和数据打交道的只有oj_mode模块,只需要更改该部分代码即可;
QueryMySQL
函数用于执行查询sql语句,并将查询结果封装成Question插入到out中;
GetAllQuestions
用于向MySQL发出查询所有题目的指令;GetOneQuestion
用于向MySQL发出查询单个题目的指令;这是构建网页的模块;
ctemplate库的github仓库
这是谷歌开源的cpp网页渲染库;
在ctemplate中数据是以字典的格式存放的
待渲染的网页中写入的是数据的key值,渲染之后将key换成对应的value;
测试网页渲染功能:
{{key}}
TemplateDictionary root
是建立ctemplate参数目录结构,相当于 unordered_map<string, string> test;
root.SetValue
向目录中添加你要替换的数据,kv的,相当于test.insert({ket, value});
GetTemplate
获取待渲染对象,DO_NOT_STRIP
是指保持html网页原貌;tpl->Expand
开始渲染,替换字典中的kv,返回新的网页结果到out_html;#include <iostream> #include <string> #include <ctemplate/template.h> using namespace std; int main() { //html网页的地址 string html = "./test.html"; string html_info = "lmx_xdu"; //建立ctemplate参数目录结构 //相当于 unordered_map<string, string> test; ctemplate::TemplateDictionary root("test"); //向目录中添加你要替换的数据,kv的 //第一个参数是key,第二个参数是value,将html中的key全部替换为value //相当于test.insert({ket, value}); root.SetValue("info", html_info); //获取待渲染对象 //DO_NOT_STRIP是指保持html网页原貌 ctemplate::Template *tpl = ctemplate::Template::GetTemplate(html, ctemplate::DO_NOT_STRIP); //开始渲染,替换字典中的kv,返回新的网页结果到out_html string out_html; tpl->Expand(&out_html, &root); cout << "渲染的带参html是:" << endl; cout << out_html << endl; return 0; }
源html网页:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <!--渲染参数,会被我们C++代码中的数据替换, info就是上面SetValue("info", html_info)代码中的 info,会自动被std::string info_html中的内容替换--> <p>{{info}}</p> <p>{{info}}</p> <p>{{info}}</p> <p>{{info}}</p> </body> </html>
渲染后的html网页:
View类用于网页的渲染;
/template_html
路径下:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>在线OJ题目列表</title> </head> <body> <table> <tr> <th>编号</th> <th>标题</th> <th>难度</th> </tr> {{#question_list}} <tr> <td>{{number}}</td> <td><a href="/question/{{number}}">{{title}}</a></td> <td>{{star}}</td> </tr> {{/question_list}} </table> </body> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{number}}.{{title}}</title>
</head>
<body>
<h4>{{number}}.{{title}}.{{star}}</h4>
<P>{{desc}}</P>
<textarea name="code" cols="100" rows="50">{{pre_code}}</textarea>
</body>
</html>
AllExpandHtml
函数将读取到的所有题目信息都渲染到网页上;
{{#question_list}}
修饰的所有内容循环渲染;OneExpandHtml
用于单个题目信息的渲染;这是业务的核心逻辑模块;
负载均衡模块用于帮助Control选取负载最低的编译服务器,所有编译服务器的配置信息都在以下文件中:
Machine
类用于保存每台编译服务器的具体信息,一个编译服务对应一个Machine
:
LoadBalance
类用于实现负载均衡算法:
Machine
,记录所有上线和下线的主机,并且有一把锁保证LoadBalance的数据安全;LoadConf
函数用于将配置文件中所有的服务器信息全部读取并保存;SmartChoice
函数用于根据所有上线服务器的负载信息,选择负载最低的机器;OfflineMachine
函数用于将指定的主机离线;OnlineMachine
函数用于上线所有已离线的主机,是将所有_offline
中的主机全部插入到_online
中,并删除_offline
中的主机;Control类用于根据Model类中获取的题目数据,来调用View类中的方法构建OJ网页;
RecoveryMachine
用于将所有的离线主机恢复为上线模式;AllQuestions
使用Model模块获取所有的题目信息,再通过View模块将题目信息渲染到网页上;Question
根据指定题目构建网页;Judge
实现判题功能,步骤如下:
in_json
进行反序列化,得到题目的id,得到用户提交的源代码,input输入参数;cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8")
oj_server:oj_server.cc
g++ -o $@ $^ -std=c++11 -L/usr/lib64/mysql/ -lpthread -ljsoncpp -lctemplate -lmysqlclient
.PHONY:clean
clean:
rm -f oj_server
R"()"
,原始字符串,保持字符串内容的原貌,不用做相关的转义;#include <iostream> #include "../Comm/httplib.h" #include "../Comm/util.hpp" using namespace httplib; int main() { //用户请求的服务路由功能 Server svr; //获取所有题目列表 svr.Get("/all_questions", [](const Request &req, Response &resp){ resp.set_content("这是所有题目的列表", "text/plain; charset=utf-8"); }); //用户要根据题目编号,获取题目的内容 //question/100 ->正则匹配 //R"()",原始字符串,保持字符串内容的原貌,不用做相关的转义 svr.Get(R"(/question/(\d+))", [](const Request &req, Response &resp){ string number = req.matches[1]; resp.set_content("这是指定的一道题: " + number, "text/plain; charset=utf-8"); }); //用户提交代码,使用我们的判题功能(1.每道题的测试用例 2.compile_and_run) svr.Get(R"(/judge/(\d+))", [](const Request &req, Response &resp){ string number = req.matches[1]; resp.set_content("指定题目的判题: " + number, "text/plain; charset=utf-8"); }); svr.set_base_dir("./wwwroot"); svr.listen("0.0.0.0", 8080); return 0; }
首页:
题目列表:
指定题目:
判题:
set_content
中的格式设置为html;首页:
题目列表:
做题界面:
添加Control对象,实现加载题目和判题功能的请求:
Recovery
重新上线所有主机;使用PostMan进行测试:
service_machine.conf
中的:index.html
all_questions.html
one_question.html
这部分包含前后端交互:
submit
函数用于获取页面上的题目信息,形成请求url,并通过ajax向后台发起基于http的json请求:show_result
函数用于得到结果,解析并显示到 result中负载均衡测试
每次都会选择负载最低的机器运行;
主机离线上线测试:
所有主机离线后,再次上线,触发ctrl + c信号,就可以上线所有主机;
顶层makefile用于项目的编译、清理和发布;
.PHONY: all all : #编译 @cd compiler_server;\ make;\ cd -;\ cd online_judge;\ make;\ cd -; #项目发布 .PHONY : output output : @mkdir -p output/compiler_server;\ mkdir -p output/online_judge;\ cp -rf compiler_server/compile_server output/compiler_server;\ cp -rf compiler_server/temp output/compiler_server;\ cp -rf online_judge/conf output/online_judge;\ cp -rf online_judge/questions output/online_judge;\ cp -rf online_judge/template_html output/online_judge;\ cp -rf online_judge/wwwroot output/online_judge;\ cp -rf online_judge/oj_server output/online_judge; #项目清理 .PHONY : clean clean : @cd compiler_server;\ make clean;\ cd -;\ cd online_judge;\ make clean;\ cd -;\ rm -rf output;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。