前面部分请看这里C++ – 负载均衡式在线OJ (二)
四、oj_server模块
oj_server说白了就是一个网站。oj_server的功能如下
- 1.获取首页
- 2.获取标题列表
- 3.获取单道标题,并提供编辑功能
- 4.提交判题功能(背后依赖的就是提供编译运行服务的服务器)
我们想采取的是基于MVC的一种架构模式
- M model 与数据交互的模块
- V view 视图,指用户界面,就是用来与用户举行交互的,模块
- C controller 控制器,焦点的业务逻辑都在这里实现,公道调配model和view模块。
oj_server负担的就是负载均衡式的去调用后端的一个个编译服务,然后显现给用户,以是oj_server更靠近用户
1. oj_server的功能路由
我们操持的oj_server一共能提供给用户的是3个功能路由
- 1.标题列表的功能路由
- 2.单道标题标功能路由
- 3.提交代码举行判题的功能路由
- #include <iostream>
- #include <signal.h>
- #include "../comm/httplib.h"
- #include "oj_control.hpp"
- using namespace httplib;
- using namespace ns_control;
- static Control *ctrl_ptr = nullptr;
- void Recovery(int signo)
- {
-
-
- ctrl_ptr->RecoveryMachine();
- }
- int main()
- {
-
-
- signal(SIGQUIT, Recovery);
- //用户请求的服务路由功能
- Server svr;
- Control ctrl;// 当用户请求时就直接调用controller当中的方法,交互数据model也被controller包含在内
- ctrl_ptr = &ctrl;
- // 获取所有的题目列表
- svr.Get("/all_questions", [&ctrl](const Request &req, Response &resp){
-
-
- //返回一张包含有所有题目的html网页
- std::string html;
- ctrl.AllQuestions(&html);
- //用户看到的是什么呢??网页数据 + 拼上了题目相关的数据
- resp.set_content(html, "text/html; charset=utf-8");
- });
- // 用户要根据题目编号,获取题目的内容
- // /question/100 -> 正则匹配
- // R"()", 原始字符串raw string,保持字符串内容的原貌,不用做相关的转义
- svr.Get(R"(/question/(\d+))", [&ctrl](const Request &req, Response &resp){
-
-
- std::string number = req.matches[1];
- std::string html;
- ctrl.Question(number, &html);
- resp.set_content(html, "text/html; charset=utf-8");
- });
- // 用户提交代码,使用我们的判题功能(1. 每道题的测试用例 2. compile_and_run)
- svr.Post(R"(/judge/(\d+))", [&ctrl](const Request &req, Response &resp){
-
-
- std::string number = req.matches[1];
- std::string result_json;
- ctrl.Judge(number, req.body, &result_json);
- resp.set_content(result_json, "application/json;charset=utf-8");
- // resp.set_content("指定题目的判题: " + number, "text/plain; charset=utf-8");
- });
-
- // 设置Web根目录
- svr.set_base_dir("./wwwroot");
- // 启动服务器
- svr.listen("0.0.0.0", 8080);
- return 0;
- }
复制代码 注意:
- 1.set_base_dir实在是提供给首页的,我们的url假如是http://101.42.249.66/的话就代表想要的资源是/,这个实在就代表的是访问的我们的web根目次(我们定名为wwwroot),而一样寻常,如许的访问代表首页,我们会在web根目次下放置一个index.html供用户访问
- 2.R"()"上面以及说过了,就是row string,保持()中字符串原貌。
- 3.然后(\d+)代表的是正则表达式,+代表有多少就匹配多少,\d是匹配数字
- 4.上面使用到了Request当中的mathes对象,实在matches对象就是将我们的资源申请做了切分,好比说\question\100,question就放到了matches[0],100就放到了matches[1]当中。
我们提供了三个功能路由就分别对应三种资源申请
- http://110.42.249.66:8080/all_questions
- http://110.42.249.66:8080/question/1
2. 创建文件版的题库
起首,我们的标题必要的东西有
- 1.题号 number
- 2.标题 title
- 3.难度 star
- 4.形貌 desc
- 5.时间要求 cpu_limit
- 6.空间要求 mem_limit
在oj_server目次下,我们必要一个questions目次对标题标全部东西举行存储。
而我们必要一个questions.list设置文件来读取全部标题(我们操持将标题构建成一个Question对象)
然后更具体的东西,好比标题标形貌,预设给用户的代码,测试用例单独放在一个目次里
在questions.list设置文件中的存储方式
header.cpp
- #include <iostream>
- #include <vector>
- #include <algorithm>
- using namespace std;
- class Solution
- {
-
-
- public:
- int Max(const vector<int> &v)
- {
-
-
- //将你的代码写在下面
- return 0;
- }
- };
复制代码 tail.cpp测试用例部分
以是我们必要给header.cpp中的代码举行归并,举行归并的代码就放在tail.cpp当中
所谓测试用例,实在就是把你在代码编辑框中的代码提交上来,然后和别的一个代码举行归并。这个代码里差的就是对你写的那部分函数。以是两个合在一起,才形成了完备的一个步调。
- #ifndef COMPILER_ONLINE
- #include "header.cpp"
- #endif
- void Test1()
- {
-
-
- vector<int> v = {
-
- 1, 2, 3, 4, 5, 6};
- int max = Solution().Max(v);
- if (max == 6)
- {
-
-
- std::cout << "Test 1 .... OK" << std::endl;
- }
- else
- {
-
-
- std::cout << "Test 1 .... Failed" << std::endl;
- }
- }
- void Test2()
- {
-
-
- vector<int> v = {
-
- -1, -2, -3, -4, -5, -6};
- int max = Solution().Max(v);
- if (max == -1)
- {
-
-
- std::cout << "Test 2 .... OK" << std::endl;
- }
- else
- {
-
-
- std::cout << "Test 2 .... Failed" << std::endl;
- }
- }
- int main()
- {
-
-
- Test1();
- Test2();
- return 0;
- }
复制代码 注意:
- 条件编译的缘故原由是:这部分代码由于缺少用户提交的那部分函数,以是我们在编译oj_server的时间,是会报错的,由于少了函数,跑不了可以明确。以是我们必要加一个条件编译,让这个.cc文件知道我们有该函数,不要报错。
- 这个条件编译到时间我们再通过给其他方式去掉,我们可以在调用g++的时间加选项,好比我们上面的宏是 COMPILER_ONLINE,那么到时间,我们直接 gcc … -D COMPILER_ONLIEN就可以去掉了。-D选项就是在下令行举行宏界说的方式
3. model模块
model模块紧张是用来和数据交互的,对外提供访问数据的接口
我们在model模块当中,由于我们的数据就是标题,以是一上来我们就要把标题读出来。
我们会有一个Question类,用它来形貌该标题标信息
- // 根据题⽬list⽂件,加载所有的题⽬信息到内存中
- // model: 主要⽤来和数据进⾏交互,对外提供访问数据的接⼝
- namespace ns_model
- {
-
-
- using namespace std;
- using namespace ns_log;
- using namespace ns_util;
- struct Question
- {
-
-
- std::string number; // 题⽬编号,唯⼀
- std::string title; // 题⽬的标题
- std::string star; // 难度: 简单 中等 困难
- int cpu_limit; // 题⽬的时间要求(S)
- int mem_limit; // 题⽬的空间要去(KB)
- std::string desc; // 题⽬的描述
- std::string header; // 题⽬预设给⽤⼾在线编辑器的代码
- std::string tail; // 题⽬的测试⽤例,需要和header拼接,形成完整代码
- };
- }
复制代码 选择用unordered_map<string,Question>的结构体来存储天生的Question,创建标题(字符串)与Question的映射。
使用boost准标准库当中的split举行字符串分割
- class StringUtil
- {
-
-
- public:
- /**
- * str:输入性参数,要切分的字符串
- * target:输出型参数,保存并返回切分完毕的结果
- * sep:separator分隔符
- */
- static void SplitString(const std::string &str,std::vector<std::string>* target,std::string sep)
- {
-
-
- //使用C++准标准库boost 当中的split进行字符串分割
- boost::split((*target),str,boost::is_any_of(sep),boost::algorithm::token_compress_on);
- //is_any_of代表sep分隔符字符串当中的任意一个字符都能用来分割
- //token_compress_on代表我是否需要进行压缩
- //调用这个接口就自动的帮我们完成了字符串切分
- }
- };
复制代码 按行读取设置文件形成Question对象
- 1.用C++的文件流的方式创建ifstream对象,打开文件流
- 2.使用getline举行按行读取,getline的注意事项上面以及说过,不再重复
- 3.使用字符串工具类中封装好的函数举行字符串切割放入tokens数组
- 4.使用该数组举行Question结构体的创建
- // 根据题⽬list⽂件,加载所有的题⽬信息到内存中
- // model: 主要⽤来和数据进⾏交互,对外提供访问数据的接⼝
- namespace ns_model
- {
-
-
- using namespace std;
- using namespace ns_log;
- using namespace ns_util;
- const std::string questins_list = "./questions/questions.list";
- const std::string questins_path = "./questions/";
- class Model
- {
-
-
- private:
- // 题号:题目细节
- unordered_map<string, Question> questions;
- public:
- Model()
- {
-
-
- assert(LoadQuestionList(questins_list));
- }
- bool LoadQuestionList(const string &question_list)
- {
-
-
- // 加载配置⽂件: questions/questions.list + 题⽬编号⽂件
- ifstream in(question_list);
- if (!in.is_open())
- {
-
-
- LOG(FATAL) << " 加载题库失败,请检查是否存在题库⽂件" << "\n";
- return false;
- }
- string line;
- while (getline(in, line))
- {
-
-
- vector<string> tokens;
- StringU
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金 |