当前位置: 首页 > news >正文

深圳 网站策划android studio手机版

深圳 网站策划,android studio手机版,dede导入wordpress,手机网站大全网址大全文章目录 项目介绍所用技术与开发环境所用技术开发环境 项目框架compiler_server模块compiler编译功能comm/util.hpp 编译时的临时文件comm/log.hpp 日志comm/util.hpp 时间戳comm/util.hpp 检查文件是否存在compile_server/compiler.hpp 编译功能总体编写 runner运行功能资源设… 文章目录 项目介绍所用技术与开发环境所用技术开发环境 项目框架compiler_server模块compiler编译功能comm/util.hpp 编译时的临时文件comm/log.hpp 日志comm/util.hpp 时间戳comm/util.hpp 检查文件是否存在compile_server/compiler.hpp 编译功能总体编写 runner运行功能资源设置comm/util.hpp 运行时的临时文件compile_server/runner.hpp 运行功能编写 compile_server/compile_run.hpp 编译且运行comm/util.hpp 生成唯一文件名comm/uti.hpp 写入文件/读出文件清理临时文件compiler_run模块的整体代码本地进行编译运行模块的整体测试 compiler_server模块打包网络服务compiler_server/compile_server.cc oj_server模块oj_server.cc 路由框架oj_model.hpp/oj_model2.hpp文件版本数据库版本 oj_view.hppoj_control.cpp 项目介绍 项目是基于负载均衡的一个在线判题系统用户自己编写代码提交给后台后台再根据负载情况选择合适的主机提供服务编译运行服务。 所用技术与开发环境 所用技术 C STL 标准库Boost 准标准库(字符串切割)cpp-httplib 第三方开源网络库ctemplate 第三方开源前端网页渲染库jsoncpp 第三方开源序列化、反序列化库负载均衡设计多进程、多线程MySQL C connectAce前端在线编辑器html/css/js/jquery/ajax 开发环境 Centos 7 云服务器vscode 项目框架 compiler_server模块 模块结构 总体流程图 compiler编译功能 在运行编译服务的时候compiler收到来自oj_server传来的代码我们对其进行编译在编译前我们需要一个code.cpp形式的文件在编译后我们会形成code.exe可执行程序若编译失败还会形成code.error来保存错误信息因此我们需要对这些文件的后缀进行添加所以我们创建temp文件夹该文件夹用来保存code代码的各种后缀所以在传给编译服务的时候只需要传文件名即可拼接路径由comm公共模块下的util.hpp提供路径拼接 comm/util.hpp 编译时的临时文件 #pragma once#include iostream#include unistd.h #include sys/stat.h #include sys/types.h #include sys/time.hnamespace ns_util {const std::string path ./temp/;// 合并路径类class PathUtil{public:static std::string splic(const std::string str1, const std::string str2){return path str1 str2;}// cpp文件 后缀名// file_name - ./temp/xxx.cppstatic std::string Src(const std::string file_name){return splic(file_name, .cpp);}// exe文件 后缀名static std::string Exe(const std::string file_name){return splic(file_name, .exe);}static std::string CompilerError(const std::string file_name){return splic(file_name, .compile_error);}}; }comm/log.hpp 日志 日志需要输出等级、文件名、行数、信息、时间 #pragma once#include string #include util.hppnamespace ns_log {using namespace ns_util;// 日志等级enum{INFO,DEBUG,WARNING,ERROR,FATAL,};inline std::ostream Log(const std::string level, const std::string file_name, int line){std::string log [;log level;log ];log [;log file_name;log ];log [;log std::to_string(line);log ]; log [;log TimeUtil::GetTimeStamp();log ]; std::cout log;return std::cout;}#define LOG(level) Log(#level, __FILE__, __LINE__) } 获取时间利用的是时间戳在util工具类中编写获取时间戳的代码。利用操作系统接口gettimeofday comm/util.hpp 时间戳 class TimeUtil { public:static std::string GetTimeStamp(){struct timeval _t;gettimeofday(_t, nullptr);return std::to_string(_t.tv_sec);} }; 进行编译服务的编写根据传入的源程序文件名子进程对stderr进行重定向到文件compile_error中使用execlp进行程序替换父进程在外面等待子进程结果等待成功后根据是否生成可执行程序决定是否编译成功; 判断可执行程序是否生成我们利用系统调用stat来查看文件属性如果有则说明生成否则失败 comm/util.hpp 检查文件是否存在 class FileUtil { public:static bool IsFileExists(const std::string path_name){// 系统调用 stat 查看文件属性// 获取属性成功返回 0struct stat st;if (stat(path_name.c_str(), st) 0){return true;}return false;} };compile_server/compiler.hpp 编译功能总体编写 #pragma once#include iostream #include sys/stat.h #include sys/types.h #include unistd.h #include wait.h #include fcntl.h#include ../comm/util.hpp #include ../comm/log.hpp// 只负责代码的编译 namespace ns_compiler {// 引入路径拼接using namespace ns_util;using namespace ns_log;class Compiler{Compiler() {}~Compiler() {}public:// 返回值是否编译成功// file_name : xxx// file_name - ./temp/xxx.cpp// file_name - ./temp/xxx.exe// file_name - ./temp/xxx.errorstatic bool Compile(const std::string file_name){pid_t id fork();if (id 0){LOG(ERROR) 内部错误当前子进程无法创建 \n;return false;}else if (id 0) // 子进程 编译程序{int _error open(PathUtil::Error(file_name).c_str(), O_CREAT | O_WRONLY, 0644);if (_error 0){LOG(WARNING) 没有成功形成 error 文件 \n;exit(1);}// 重定向标准错误到 _errordup2(_error, 2);// g -o target src -stdc11execlp(g, g, -o, PathUtil::Exe(file_name).c_str(),PathUtil::Src(file_name).c_str(), -stdc11, nullptr);LOG(ERROR) g执行失败检查参数是否传递正确 \n;exit(2);}else // 父进程 判断编译是否成功{waitpid(id, nullptr, 0);if (FileUtil::IsFileExists(PathUtil::Exe(file_name))){LOG(INFO) PathUtil::Exe(file_name) 编译成功 \n;return true;}LOG(ERROR) 编译失败 \n;return false;}}};} runner运行功能 编译完成后我们就可以执行可执行程序了执行前首先打开三个文件xxx.stdin,xxx.stdout,xxx.stderr并将标准输入、标准输出和标准错误分别重定向到三个文件中。创建子进程来进行程序替换执行程序每道题的代码运行时间和内存大小都有限制所以在执行可执行程序之前我们对内存和时间进行限制。 资源设置 利用setrlimit系统调用来实现 int setrlimit(int resource, const struct rlimit *rlim);static void SetProcLimit(int cpu_limit, int mem_limit){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;mem_rlimit.rlim_max RLIM_INFINITY;setrlimit(RLIMIT_AS, mem_rlimit);}comm/util.hpp 运行时的临时文件 static std::string Stdin(const std::string file_name){return splic(file_name, .stdin);}static std::string Stdout(const std::string file_name){return splic(file_name, .stdout);}// error文件 后缀名static std::string Stderr(const std::string file_name){return splic(file_name, .stderr);}compile_server/runner.hpp 运行功能编写 #pragma once #include iostream #include sys/stat.h #include sys/types.h #include sys/wait.h #include unistd.h #include wait.h #include fcntl.h #include sys/resource.h#include ../comm/util.hpp #include ../comm/log.hppnamespace ns_runner {using namespace ns_log;using namespace ns_util;class Runner{public:Runner() {}~Runner() {}static void SetProcLimit(int cpu_limit, int mem_limit){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;mem_rlimit.rlim_max RLIM_INFINITY;setrlimit(RLIMIT_AS, mem_rlimit);}// 指明文件名即可无后缀、无路径// 返回值 // 0 内部错误 // 0运行成功成功写入stdout等文件 // 0运行中断用户代码存在问题static int Run(const std::string file_name, int cpu_limit, int mem_limit){// 运行程序会有三种结果/* 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;}pid_t id fork();if(id 0){ close(_stdin_fd);close(_stdout_fd);close(_stderr_fd);LOG(ERROR) 内部错误, 创建子进程失败 \n;return -2;}else if(id 0) // 子进程{dup2(_stdin_fd, 0);dup2(_stdout_fd, 1);dup2(_stderr_fd, 2); SetProcLimit(cpu_limit, mem_limit);execl(_execute.c_str(), /*要执行谁*/ _execute.c_str(), /*命令行如何执行*/ nullptr);exit(1);}else // 父进程{close(_stdin_fd);close(_stdout_fd);close(_stderr_fd);int status 0;waitpid(id, status, 0);LOG(INFO) 运行完毕退出码为: (status 0x7F) \n;return status 0x7f;}}}; } compile_server/compile_run.hpp 编译且运行 用户的代码会以json串的方式传给该模块给每一份代码创建一个文件名具有唯一性的源文件调用上面的编译和运行执行该源文件再把结果构建成json串返回给上层 json串的结构 comm/util.hpp 生成唯一文件名 当一份用户提交代码后我们为其生成的源文件名需要具有唯一性。名字生成唯一性我们可以利用毫秒级时间戳加上原子性的增长计数实现 获取毫秒时间戳在TimeUtil工具类中生成唯一文件名在FileUtil工具类中 static std::string GetTimeMs(){struct timeval _time;gettimeofday(_time, nullptr);return std::to_string(_time.tv_sec * 1000 _time.tv_usec / 1000);}comm/uti.hpp 写入文件/读出文件 因为需要填写运行成功结果和运行时报错的结果所以我们写一个写入文件和读出文件放在FileUtil中 static bool WriteFile(const std::string target, const std::string content){std::ofstream out(target);if (!out.is_open()){return false;}out.write(content.c_str(), content.size());out.close();return true;}// 根据路径文件进行读出// 注意默认每行的\\n是不进行保存的需要保存请设置参数static bool ReadFile(const std::string path_file, std::string *content, bool keep false){// 利用C的文件流进行简单的操作std::string line;std::ifstream in(path_file);if (!in.is_open())return ;while (std::getline(in, line)){(*content) line;if (keep)(*content) \n;}in.close();return true;}清理临时文件 编译还是运行都会生成临时文件所以可以在编译运行的最后清理一下这一次服务生成的临时文件 static void RemoveTempFile(const std::string file_name){// 因为临时文件的存在情况存在多种删除文件采用系统接口unlink但是需要判断std::string src_path PathUtil::Src(file_name);if (FileUtil::IsFileExists(src_path))unlink(src_path.c_str());std::string stdout_path PathUtil::Stdout(file_name);if (FileUtil::IsFileExists(stdout_path))unlink(stdout_path.c_str());std::string stdin_path PathUtil::Stdin(file_name);if (FileUtil::IsFileExists(stdin_path))unlink(stdin_path.c_str());std::string stderr_path PathUtil::Stderr(file_name);if (FileUtil::IsFileExists(stderr_path))unlink(stderr_path.c_str());std::string compilererr_path PathUtil::CompilerError(file_name);if (FileUtil::IsFileExists(compilererr_path))unlink(compilererr_path.c_str());std::string exe_path PathUtil::Exe(file_name);if (FileUtil::IsFileExists(exe_path))unlink(exe_path.c_str());}提供一个Start方法让上层调用编译运行模块参数是一个输入形式的json串和一个要给上层返回的json串 使用jsoncpp反序列化解析输入的json串。调用形成唯一文件名的方法生成一个唯一的文件名然后使用解析出来的代码部分创建出一个源文件把文件名交给编译模块进行编译再把文件名和时间限制内存限制传给运行模块运行记录这个过程中的状态码。再最后还要序列化一个json串返还给用户更具获得状态码含义的接口填写状态码含义根据状态码判断是否需要填写运行成功结果和运行时报错的结果然后把填好的结果返还给上层。 最终调用一次清理临时文件接口把这一次服务生成的所有临时文件清空即可。 两个json的具体内容 compiler_run模块的整体代码 #pragma once #include jsoncpp/json/json.h#include compiler.hpp #include runner.hpp #include ../comm/util.hpp #include ../comm/log.hppnamespace ns_complie_and_run {using namespace ns_log;using namespace ns_util;using namespace ns_compiler;using namespace ns_runner;class ComplieAndRun{public:static void RemoveTempFile(const std::string file_name){// 因为临时文件的存在情况存在多种删除文件采用系统接口unlink但是需要判断std::string src_path PathUtil::Src(file_name);if (FileUtil::IsFileExists(src_path))unlink(src_path.c_str());std::string stdout_path PathUtil::Stdout(file_name);if (FileUtil::IsFileExists(stdout_path))unlink(stdout_path.c_str());std::string stdin_path PathUtil::Stdin(file_name);if (FileUtil::IsFileExists(stdin_path))unlink(stdin_path.c_str());std::string stderr_path PathUtil::Stderr(file_name);if (FileUtil::IsFileExists(stderr_path))unlink(stderr_path.c_str());std::string compilererr_path PathUtil::CompilerError(file_name);if (FileUtil::IsFileExists(compilererr_path))unlink(compilererr_path.c_str());std::string exe_path PathUtil::Exe(file_name);if (FileUtil::IsFileExists(exe_path))unlink(exe_path.c_str());}// 0进程收到信号导致异常崩溃// 0整个过程非运行报错// 0整个过程全部完成static std::string CodeToDesc(int status, const std::string file_name){std::string desc;switch (status){case 0:desc 运行成功;break;case -1:desc 代码为空;break;case -2:desc 未知错误;break;case -3:desc 编译报错\n;FileUtil::ReadFile(PathUtil::CompilerError(file_name), desc, true);break;case 6:desc 内存超过范围;break;case 24:desc 时间超时;break;case 8:desc 浮点数溢出;break;case 11:desc 野指针错误;break;default:desc 未处理的报错-status为: std::to_string(status);break;}return desc;}/*输入code: 用户提交的代码input: 用户提交的代码对应的输入cpu_limit:mem_limit:输出必有,status: 状态码reason: 请求结果可能有stdout: 运行完的结果stderr: 运行完的错误*/static void Start(const std::string in_json, std::string *out_json){Json::Value in_value;Json::Reader reader;reader.parse(in_json, in_value, 1);std::string code in_value[code].asString();std::string input in_value[input].asString();int cpu_limit in_value[cpu_limit].asInt();int mem_limit in_value[mem_limit].asInt();int status_code 0;Json::Value out_value;int run_result 0;std::string file_name;if (!code.size()){status_code -1; // 代码为空goto END;}// 毫秒级时间戳 原子性递增唯一值来保证唯一性file_name FileUtil::UniqFileName();if (!FileUtil::WriteFile(PathUtil::Src(file_name), code)){status_code -2; // 未知错误goto END;}if (!Compiler::Compile(file_name)){status_code -3; // 未知错误goto END;}run_result Runner::Run(file_name, cpu_limit, mem_limit);if (run_result 0)status_code -2; // 未知错误else if (run_result 0)status_code run_result; // 崩溃elsestatus_code 0;END:out_value[status] status_code;out_value[reason] CodeToDesc(status_code, file_name);if (status_code 0){// 整个过程全部成功std::string _stdout;FileUtil::ReadFile(PathUtil::Stdout(file_name), _stdout, true);out_value[stdout] _stdout;std::string _stderr;FileUtil::ReadFile(PathUtil::Stderr(file_name), _stderr, true);out_value[stderr] _stderr;}Json::StyledWriter writer;*out_json writer.write(out_value);RemoveTempFile(file_name);}}; }本地进行编译运行模块的整体测试 自己手动构造一个json串编译、运行、返回结果json串 #include compile_run.hppusing namespace ns_complie_and_run;// 编译服务会被同时请求保证代码的唯一性 int main() {// 客户端请求jsonstd::string in_json;Json::Value in_value;in_value[code] R( #includeiostreamint main() {std::cout Hello, world! std::endl;int *p new int[1024 * 1024 * 20 ];return 0;});in_value[input] ; in_value[cpu_limit] 1; in_value[mem_limit] 10240; Json::FastWriter writer;in_json writer.write(in_value);std::cout in_json: std::endl in_json std::endl;std::string out_json;ComplieAndRun::Start(in_json, out_json);std::cout out_json: std::endl out_json std::endl;return 0; }compiler_server模块打包网络服务 编译运行服务已经整合在一起了接下来将其打包成网络服务即可 我们利用httplib库将compile_run打包为一个网络编译运行服务 compiler_server/compile_server.cc 使用了 httplib 库来提供 HTTP 服务实现了一个编译运行服务器通过命令行参数接收端口号一个POST /compile_and_run主要的编译运行接口接收JSON格式的请求体包含代码内容、输入数据、CPU 限制、内存限制 #include compile_run.hpp #include ../comm/httplib.husing namespace ns_compile_and_run; using namespace httplib;void Usage(std::string proc) {std::cerr Usage : \n\t proc prot std::endl; }int main(int argc, char* argv[]) {if(argc ! 2){Usage(argv[0]);return 1;}Server svr;svr.Post(/compile_and_run, [](const Request req, Response resp){std::string in_json req.body;std::string out_json;if(!in_json.empty()){CompileAndRun::Start(in_json, out_json);resp.set_content(out_json, application/json;charsetutf-8);}});svr.listen(0.0.0.0, atoi(argv[1])); // 启动 http 服务return 0; }oj_server模块 oj_server.cc 路由框架 步骤 服务器初始化: 创建 HTTP 服务器实例初始化控制器设置信号处理函数 请求处理: 接收 HTTP 请求根据 URL 路由到对应处理函数调用控制器相应方法返回处理结果 判题流程: 接收用户提交的代码通过控制器进行判题返回判题结果 创建一个服务器对象 int main() {Server svr; // 服务器对象 }获取所有题目列表 返回所有题目的HTML页面 svr.Get(/all_questions, [ctrl](const Request req, Response resp){std::string html;ctrl.AllQuestions(html);resp.set_content(html, text/html; charsetutf-8); });获取单个题目 返回单个题目的详细信息页面 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; charsetutf-8); });提交代码判题 处理用户提交的代码 返回 JSON 格式的判题结果 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;charsetutf-8); });服务器配置和启动 svr.set_base_dir(./wwwroot); svr.listen(0.0.0.0, 8080);维护一个全局控制器指针 Recovery 函数处理 SIGQUIT 信号用于服务器恢复 static Control *ctrl_ptr nullptr;void Recovery(int signo) {ctrl_ptr-RecoveryMachine(); }oj_model.hpp/oj_model2.hpp 整体架构为MVC模式 Model层 由oj_model.hpp文件版本和 oj_model2.hpp数据库版本构成 负责数据的存储和访问提供了两种实现方式 基础数据结构设计 struct Question {string number; // 题目编号string title; // 题目标题string star; // 难度等级int cpu_limit; // CPU时间限制秒int mem_limit; // 内存限制KBstring desc; // 题目描述string header; // 用户代码模板string tail; // 测试用例代码 };存储方案设计 文件版本 优势 简单直观易于管理 适合小规模题库 方便备份和版本控制 劣势 并发性能较差 扩展性有限 数据一致性难保证 目录结构 ./questions/├── questions.list # 题目基本信息└── 1/ # 每个题目独立目录├── desc.txt # 题目描述├── header.cpp # 代码模板└── tail.cpp # 测试用例具体代码 #pragma once#include ../comm/log.hpp #include ../comm/util.hpp#include iostream #include string #include unordered_map #include cassert #include vector #include fstream #include cstdlib #include boost/algorithm/string.hpp// 根据题目 list 文件加载所有题目的信息到内存中 // model主要用来和数据进行交互对外提供访问数据的接口namespace ns_model {using namespace std;using namespace ns_log;using namespace ns_util;struct Question{string number; // 题目编号string title; // 题目的标题string star; // 题目的难度int cpu_limit; // 题目的时间要求(s)int mem_limit; // 题目的空间要求(KB)string desc; // 题目的描述string header; // 题目给用户的部分代码string tail; // 题目的测试用例和 header 形成完整代码提交给后端编译};const string questions_list ./questions/questions.list ;const string question_path ./questions/ ;class Model{private:// 【题号 - 题目细节】unordered_mapstring, Question questions;public:Model(){assert(LoadQuestionList(questions_list));}bool LoadQuestionList(const string question_list){// 加载配置文件 : questions/questions.list 题目编号文件ifstream in(question_list);if(!in.is_open()) {LOG(FATAL) 题目加载失败请检查是否存在题库文件 std::endl;return false;}std::string line;while(getline(in, line)){ vectorstring tokens;StringUtil::SplitString(line, tokens, );if(tokens.size() ! 5){LOG(WARNING) 加载部分题目失败请检查文件格式 std::endl;continue;}Question q;q.number tokens[0];q.title tokens[1];q.star tokens[2];q.cpu_limit atoi(tokens[3].c_str());q.mem_limit atoi(tokens[4].c_str());string path question_path;path q.number;path /;FileUtil::ReadFile(pathdesc.txt, (q.desc), true);FileUtil::ReadFile(pathheader.cpp, (q.header), true);FileUtil::ReadFile(pathtail.cpp, (q.tail), true);questions.insert({q.number, q});} LOG(INFO) 加载题目成功 std::endl;in.close();return true;}bool GetAllQuestions(vectorQuestion *out){if(questions.size() 0) {LOG(ERROR) 用户获取题库失败 std::endl;return false;}for(const auto q : questions)out-push_back(q.second);return true;}bool GetOneQuestion(const string number, Question *q){const auto iter questions.find(number);if(iter questions.end()) {LOG(ERROR) 用户获取题库失败题目编号为 number std::endl;return false;}(*q) iter-second;return true;}~Model(){}}; }数据库版本 优势 更好的并发性能 事务支持保证数据一致性 表设计 CREATE TABLE oj_questions (number VARCHAR(20) PRIMARY KEY,title VARCHAR(255) NOT NULL,star VARCHAR(20) NOT NULL,description TEXT,header TEXT,tail TEXT,cpu_limit INT,mem_limit INT );#pragma once#include ../comm/log.hpp #include ../comm/util.hpp#include iostream #include string #include unordered_map #include cassert #include vector #include fstream #include cstdlib #include boost/algorithm/string.hpp #include include/mysql.h// 根据题目 list 文件加载所有题目的信息到内存中 // model主要用来和数据进行交互对外提供访问数据的接口namespace ns_model {using namespace std;using namespace ns_log;using namespace ns_util;struct Question{string number; // 题目编号string title; // 题目的标题string star; // 题目的难度string desc; // 题目的描述string header; // 题目给用户的部分代码string tail; // 题目的测试用例和 header 形成完整代码提交给后端编译int cpu_limit; // 题目的时间要求(s)int mem_limit; // 题目的空间要求(K)};const std::string oj_question ***;const std::string host ***;const std::string user ***;const std::string passwd ***;const std::string db ***;const int port 3306;class Model{public:Model(){}bool QueryMysql(const std::string sql, vectorQuestion *out){ // 创建MySQL句柄MYSQL* my mysql_init(nullptr);// 连接数据库//if(nullptr mysql_real_connect(my, host.c_str(), user.c_str(), db.c_str(), passwd.c_str(), port, nullptr, 0))if(nullptr mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(),db.c_str(), port, nullptr, 0)){std::cout mysql_error(my) std::endl;LOG(FATAL) 连接数据库失败 \n;return false;}LOG(INFO) 连接数据库成功 \n;// 设置链接的编码格式默认是拉丁的mysql_set_character_set(my, utf8);// 执行sql语句//if(0 ! mysql_query(my, sql.c_str()))if(0 ! mysql_query(my, sql.c_str())){LOG(WARNING) sql execute error! \n;return false;} // 提取结果//MYSQL_RES *res mysql_store_result(my);MYSQL_RES *res mysql_store_result(my);// 分析结果int rows mysql_num_rows(res);// 获得行数int cols mysql_num_fields(res);// 获得列数struct Question q;for(int i 0; i rows; i){MYSQL_ROW row mysql_fetch_row(res);q.number row[0];q.title row[1];q.star row[2];q.desc row[3];q.header row[4];q.tail row[5];q.cpu_limit atoi(row[6]);q.mem_limit atoi(row[7]);out-push_back(q);}// 释放结果空间free(res);// 关闭MySQL连接//mysql_close(my);mysql_close(my);return true;}bool GetAllQuestions(vectorQuestion *out){std::string sql select * from oj.;sql oj_question;return QueryMysql(sql, out);}bool GetOneQuestion(const string number, Question *q){bool res false;std::string sql select * from oj.;sql oj_question;sql where number ;sql number;vectorQuestion result;if (QueryMysql(sql, result)){if (result.size() 1){*q result[0];res true;}}return res;}~Model(){}}; }接口设计 返回bool表示操作成功与否 class Model { public:// 获取所有题目bool GetAllQuestions(vectorQuestion *out);// 获取单个题目bool GetOneQuestion(const string number, Question *q); };oj_view.hpp View层 由oj_view.hpp构成 使用 ctemplate库来进行 HTML模板渲染 使用 TemplateDictionary存储渲染数据使用 Template::GetTemplate加载模板使用 Expand方法进行渲染 获取所有题目的渲染 void AllExpandHtml(const vectorstruct Question questions, std::string *html)设置模板文件路径 (all_questions.html)创建模板字典遍历所有题目为每个题目添加 题号 (number)标题 (title)难度等级 (star) 渲染模板 获取单个题目的渲染 void OneExpandHtml(const struct Question q, std::string *html)设置模板文件路径 (one_question.html)创建模板字典并设置值 题号 (number)标题 (title)难度等级 (star)题目描述 (desc)预设代码 (header) 渲染模板 #pragma once#include iostream #include string #include ctemplate/template.h// #include oj_model.hpp #include oj_model2.hppnamespace ns_view {using namespace ns_model;const std::string template_path ./template_html/;class View{public:View() {};~View() {};public:void AllExpandHtml(const vectorstruct Question questions, std::string *html){// 题目的编号 题目的标题 题目的难度// 推荐使用表格显示// 1. 形成路径std::string src_html template_path all_questions.html;// 2. 形成数据字典ctemplate::TemplateDictionary root(all_questions);for (const auto q : questions){ctemplate::TemplateDictionary *sub root.AddSectionDictionary(question_list);sub-SetValue(number, q.number);sub-SetValue(title, q.title);sub-SetValue(star, q.star);}// 3. 获取被渲染的htmlctemplate::Template *tpl ctemplate::Template::GetTemplate(src_html, ctemplate::DO_NOT_STRIP);// 4. 开始完成渲染功能tpl-Expand(html, root);}void OneExpandHtml(const struct Question q, std::string *html){// 1. 形成路径std::string src_html template_path one_question.html;// 2. 形成数字典ctemplate::TemplateDictionary root(one_question);root.SetValue(number, q.number);root.SetValue(title, q.title);root.SetValue(star, q.star);root.SetValue(desc, q.desc);root.SetValue(pre_code, q.header);//3. 获取被渲染的htmlctemplate::Template *tpl ctemplate::Template::GetTemplate(src_html, ctemplate::DO_NOT_STRIP);//4. 开始完成渲染功能tpl-Expand(html, root);}}; }oj_control.cpp Controller层 由oj_control.hpp构成; 提供服务的主机 Machine 类 表示提供编译服务的主机 包含 IP、端口、负载信息 提供负载管理方法(增加、减少、重置、获取负载) // 提供服务的主机class Machine{public:std::string ip;int port;uint64_t load;std::mutex *mtx;public:Machine() : ip(), port(0), load(0), mtx(nullptr){}~Machine(){}public:// 提升主机负载void IncLoad(){if (mtx)mtx-lock();load;if (mtx)mtx-unlock();}// 减少主机负载void DecLoad(){if (mtx)mtx-lock();load--;if (mtx)mtx-unlock();}void ResetLoad(){ if (mtx)mtx-lock();load 0;if (mtx)mtx-unlock();}// 获取主机负载uint64_t Load(){uint64_t _load 0;if (mtx)mtx-lock();_load load;if (mtx)mtx-unlock();return _load;}};LoadBlance 类 (负载均衡模块) 管理多台编译服务器 维护在线/离线主机列表 主要功能 从配置文件加载主机信息 智能选择负载最低的主机 处理主机上线/离线 // 负载均衡模块class LoadBlance{private:// 提供编译的主机// 每一台都有自己下标std::vectorMachine machines;// 所有在线的主机 idstd::vectorint online;// 所有离线的主机 idstd::vectorint offline;// 保证 LoadBlance 的数据安全std::mutex mtx;public:LoadBlance(){assert(LoadConf(machine_path));LOG(INFO) 加载 machine_path 成功 \n;}~LoadBlance(){}public:bool LoadConf(const std::string machine_list){std::ifstream in(machine_list);if (!in.is_open()){LOG(FATAL) 主机加载失败 \n;return false;}std::string line;while (std::getline(in, line)){std::vectorstd::string tokens;StringUtil::SplitString(line, tokens, );if (tokens.size() ! 2){LOG(WARNING) 切分 line 失败 \n;continue;}Machine m;m.ip tokens[0];m.port atoi(tokens[1].c_str());m.load 0;m.mtx new std::mutex();online.push_back(machines.size());machines.push_back(m);}in.close();return true;}// id: 输出型参数// m: 输出型参数bool SmartChoice(int *id, Machine **m){// 1. 使用选择好的主机更新负载// 2. 我们可能需要离线该主机mtx.lock();// 负载均衡的算法// 1. 随机数法// 2. 轮询 hashint online_num online.size();if (online_num 0){LOG(FATAL) 所有的主机挂掉在线主机数量: online_num , 离线主机数量: offline.size() \n;mtx.unlock();return false;}// 找负载最小的主机*id online[0];*m machines[online[0]];uint64_t min_load machines[online[0]].Load();for (int i 0; i online_num; i){uint64_t cur_load machines[online[i]].Load();if (cur_load min_load){min_load cur_load;*id online[i];*m machines[online[i]];}}mtx.unlock();return true;}void OfflineMachine(int id){mtx.lock();for(auto it online.begin(); it ! online.end(); it){if(*it id){machines[id].ResetLoad();// 离线主机已经找到online.erase(it);offline.push_back(*it);break;}}mtx.unlock();}void OnlineMachine(){// 当所有主机离线后统一上线mtx.lock();online.insert(online.end(), offline.begin(), offline.end()); offline.erase(offline.begin(), offline.end());mtx.unlock();LOG(INFO) 所有的主机已上线 \n;}void ShowMachine(){mtx.lock();std::cout 在线主机列表: \n;for(auto it : online){std::cout it ;}std::cout std::endl;std::cout 离线主机列表: \n;for(auto it : offline){std::cout it ;}std::cout std::endl;mtx.unlock();}}; Control 类 (核心控制器) 整合 Model(数据层)和 View(视图层) 判题 // 控制器class Control{private:Model _model; // 提供后台数据View _view; // 提供网页渲染LoadBlance _load_blance;public:Control(){}~Control(){}public:void RecoveryMachine(){_load_blance.OnlineMachine();}// 根据题目数据构建网页// html 输出型参数bool AllQuestions(string *html){bool ret true;vectorstruct Question all;if (_model.GetAllQuestions(all)){sort(all.begin(), all.end(), [](const struct Question q1, const struct Question q2){return q1.number q2.number;});_view.AllExpandHtml(all, html);}else{*html 获取题目失败形成题目列表失败;ret false;}return ret;}bool Question(const std::string number, string *html){bool ret true;struct Question q;if (_model.GetOneQuestion(number, q)){_view.OneExpandHtml(q, html);}else{*html 指定题目 number 不存在;ret false;}return ret;}void Judge(const std::string number, const std::string in_json, std::string *out_json){// 0.根据题目编号拿到题目细节struct Question q;_model.GetOneQuestion(number, q);// 1.in_json 进行反序列话得到题目的 id 得到用户提交的源代码 inputJson::Reader reader;Json::Value in_value;reader.parse(in_json, in_value);std::string code in_value[code].asString();// 2.重新拼接用户的代码 测试用例形成新的代码Json::Value compile_value;compile_value[input] in_value[input].asString();compile_value[code] code \n q.tail;compile_value[cpu_limit] q.cpu_limit;compile_value[mem_limit] q.mem_limit;Json::FastWriter writer;std::string compile_string writer.write(compile_value);// 3.选择负载最低的主机while (true){Machine *m nullptr;int id 0;if (!_load_blance.SmartChoice(id, m)){break;}LOG(INFO) 选择主机成功, id id 详情 m-ip : m-port \n;// 4.发起 http 请求得到结果Client cli(m-ip, m-port);m-IncLoad();if(auto res cli.Post(/compile_and_run, compile_string, application/json;charsetutf-8)){// 5.将结果赋值给 out_jsonif(res-status 200){*out_json res-body;m-DecLoad();LOG(INFO) 请求编译、运行成功 \n;break;}m-DecLoad();}else{// 请求失败LOG(ERROR) 当前请求主机id id 详情 m-ip : m-port 该主机可能已经离线 \n;_load_blance.OfflineMachine(id);_load_blance.ShowMachine(); // for test}}}};首先 AllQuestions(): 获取并展示所有题目列表Question(): 获取并展示单个题目详情 其次Judge 1. 获取题目信息 2. 解析用户提交的代码 3. 组装完整的测试代码 4. 选择负载最低的编译主机 5. 发送HTTP请求到编译主机 6. 处理编译运行结果然后负载均衡处理使用最小负载优先算法 基本编译运行提交代码已经实现后续还会增加其他功能
http://www.hkea.cn/news/14527468/

相关文章:

  • 东莞网站建设是什么意思大淘客构建自己的网站
  • 电子商务网站建设新手房屋建筑图纸设计
  • 跑腿网站建设网络科技公司起名大全参考
  • 兰州网站外包西安广告设计与制作公司
  • ftp网站建立竞猜网站开发多少钱
  • 网站制作培训课程怎么做汽车网站
  • 昆明seocn整站优化杭州哪家网站建设公司好
  • 襄阳网站建设哪个好成都展览展示有限公司
  • 北京网站案例现在找个网站这么难的吗
  • 网站建设公司客户开发手册在国外做购物网站
  • 电商在线设计网站自助建微网站
  • 河南网站建设网络公司微信网站建设方案
  • 外贸做的社交网站有哪些做汽车销售要了解的网站
  • 帮人做网站的公司广州公关公司排名
  • 网页页面设计报价网站关键词优化代理
  • 青海网站开发建设软件开发计划模板
  • 邮箱登陆嵌入网站app编程软件有哪些
  • 嘉兴网站托管网站建设需要的网络技术
  • 安全的赣州网站建设wordpress 企业站开发
  • 去年做哪些网站能致富免费个人简历模板网站
  • 手机网站怎么放到桌面上英文网站 建站
  • 做网站百度关键排名网站在线建站
  • 有哪些官方网站做的比较好宁河集团网站建设
  • 网站开发结束语盘锦网站建设公司
  • 学网站开发需要报培训机构吗杭州网站seo优化
  • 建设项目网站备案申请表青岛找网站建设公司哪家好
  • 北京公司建站模板自己做一元购网站
  • 网站服务器地址怎么查询河东网站建设
  • 浏阳网站建设公司对门户网站建设情况的报告
  • 可以自己做漫画的网站域名换了网站需要备案么