您的位置:首页 > 房产 > 家装 > 长春火车站时刻表_代理记账公司哪家好_微信推广广告在哪里做_快推达seo

长春火车站时刻表_代理记账公司哪家好_微信推广广告在哪里做_快推达seo

2024/12/26 23:27:27 来源:https://blog.csdn.net/AAlykk/article/details/143988661  浏览:    关键词:长春火车站时刻表_代理记账公司哪家好_微信推广广告在哪里做_快推达seo
长春火车站时刻表_代理记账公司哪家好_微信推广广告在哪里做_快推达seo

> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:能自己实现负载均衡式在线 OJ。

> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!

> 专栏选自:实战项目_დ旧言~的博客-CSDN博客

> 望小伙伴们点赞👍收藏✨加关注哟💕💕

一、整体框架

总结:

先运行 oj_server,用来提供前端页面(前端页面向后端发送http请求),后端运行多个 compile_server 编译主机,oj_server 接收到请求后把请求再转发给 compile_server,compile_server经过编译运行后把结果响应回去,最终反映到前端页面上。

本项目四个核心模块构成:

  1. 公共模块:存放各个模块都能用到的公共文件。
  2. 编译与运行模块(可视为服务端):适配用户请求,负责完成基于网络请求式的编译并运行服务。
  3. OJ相关模块(可视为客户端):完成连接数据库、获取题目列表,查看题目、编写题目界面,负载均衡等后端核心业务逻辑。
  4. 项目发布模块:只包含整合后的二进制可执行编译运行文件与OJ系统网页文件,专供用户使用。

二、所用技术和开发环境

  • C++ STL:标准库.
  • Boost:准标准库(字符串切割).
  • cpp-httplib:第三⽅开源⽹络库.
  • ctemplate:第三⽅开源前端网页渲染库.
  • jsoncpp:第三⽅开源序列化、反序列化库.
  • 负载均衡设计.
  • 多进程、多线程.
  • MySQL C connect.
  • Ace前端在线编辑器(了解).
  • html/css/js/jquery/ajax (了解).

三、编写思路

  1. 先编写compile_server.
  2. 再编写oj_server.
  3. 基于文件版的负载均衡式在线 Oj.
  4. 前端网页设计.
  5. 基于MySQL版的负载均衡式在线 Oj.

四、compile_server 服务设计

提供的服务:

  • 编译并运行代码,得到格式化的相关结果。

4.1、第一个功能: Compile 编译

compiler.hpp文件:

#pragma once#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>#include "../comm/utils.hpp"
#include "../comm/log.hpp"// 只进行编译
namespace ns_compiler
{
using namespace ns_utils;
using namespace ns_log;
class Compiler
{
public:Compiler() {}~Compiler() {}// 返回值,编译成功:true,否则:falsestatic bool Compile(const std::string &filename){pid_t pid = fork(); // 子进程进行程序替换来编译这个文件if (pid < 0){LOG(ERROR) << "内部错误,创建子进程失败" << "\n";return false;}else if (pid == 0){// 子进程:调用编译器,进行程序替换执行文件umask(0); // 重置掩码// 创建错误信息文件,用来保存错误信息int errfd = open(PathUtil::Stderr(filename).c_str(), O_CREAT | O_WRONLY, 0644);// std::cout << "debug ===== " << std::endl;if (errfd < 0){LOG(ERROR) << "内部错误,打开文件失败" << "\n";exit(1);}// 重定向标准错误到errfd,以至于打印到显示器的信息,打印到Stderr文件dup2(errfd, 2);// g++ -o 可执行文件 源文件 -std=c++11execlp("g++", "g++", "-o", PathUtil::Exe(filename).c_str(), PathUtil::Src(filename).c_str(), "-std=c++11", nullptr /*不要忘记*/);exit(2); // 执行完就退出}else{// 父进程waitpid(pid, nullptr, 0);// 判断可执行文件在不在,如果在,就说明编译成功,否则失败if (FileUtil::IsExistFile(PathUtil::Exe(filename).c_str())){LOG(INFO) << PathUtil::Src(filename) << " 编译成功" << "\n";return true;}}LOG(ERROR) << "编译失败,没有形成可执行程序" << "\n";return false;}
};
}

Log.hpp文件:

#pragma once#include <iostream>#include "utils.hpp"namespace ns_log
{
using namespace ns_utils;// 日志等级
enum
{INFO, // 就是整数DEBUG,WARNING,ERROR,FATAL
};// 频繁使用,使用内联函数
inline std::ostream &Log(const std::string &level, const std::string &filename, int line)
{std::string message = "[";message += level + "]";message += "[";message += filename + "]";message += "[";message += std::to_string(line) + "]";message += "[";message += TimeUtil::GetCurrentTime() + "]";std::cout << message;return std::cout;
}// LOG(INFO) << "message"; // 开放式日志#define LOG(level) Log(#level, __FILE__, __LINE__)
}

4.2、第二个功能: Run 运行

test_setrlimit文件:测试使用限制资源函数

#include <iostream>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <signal.h>void handler(int sig)
{std::cout << "sig : " << sig << std::endl;exit(-1);
}int main()
{for (int i = 1; i < 32; ++i){signal(i, handler);}// // 限制cpu使用时间// struct rlimit r_cpu;// r_cpu.rlim_cur = 1; // 1秒// r_cpu.rlim_max = RLIM_INFINITY;// setrlimit(RLIMIT_CPU, &r_cpu);// while (1)//     ;// 限制开辟空间大小struct rlimit r_mem;r_mem.rlim_cur = 1024 * 1024 * 40; // 40Mr_mem.rlim_max = RLIM_INFINITY;setrlimit(RLIMIT_AS, &r_mem);int count = 0;while (1){int *p = new int[1024 * 1024];++count;std::cout << "count : " << count << std::endl;sleep(1);}return 0;
}

这里测试这两个终止信号:

# cpu时间限制信号:24
xp2@Xpccccc:~/Items/Load_Balancing_Online_Judging$ ./a.out 
sig : 24# 申请内存限制信号:6
xp2@Xpccccc:~/Items/Load_Balancing_Online_Judging$ ./a.out 
count : 1
count : 2
count : 3
count : 4
count : 5
count : 6
count : 7
count : 8
terminate called after throwing an instance of 'std::bad_alloc'what():  std::bad_alloc
sig : 6
Aborted (core dumped)# 信号列表
xp2@Xpccccc:~/Items/Load_Balancing_Online_Judging$ kill -l
1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

runner.hpp文件:

#pragma once#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>#include "../comm/log.hpp"
#include "../comm/utils.hpp"namespace ns_runner
{using namespace ns_log;using namespace ns_utils;class Runner{public:static void SetRlimit(int cpu_limit, int mem_limit){// 限制cpu使用时间struct rlimit r_cpu;r_cpu.rlim_cur = cpu_limit; // 1秒r_cpu.rlim_max = RLIM_INFINITY;setrlimit(RLIMIT_CPU, &r_cpu);// 限制开辟空间大小struct rlimit r_mem;r_mem.rlim_cur = mem_limit * 1024; // KBr_mem.rlim_max = RLIM_INFINITY;setrlimit(RLIMIT_AS, &r_mem);}public:// 信号 ,0表示运行成功;-1表示内部错误,其他就是对应的信号值/*filename: 文件名,不带后缀cpu_limit: cpu限制使用时间(秒)mem_limit: 内存开辟限制(KB)*/static int runner(std::string &filename, int cpu_limit, int mem_limit){/*因为运行可执行文件,需要保存运行结果运行结果:1.执行失败 2.执行成功1.执行失败 把错误信息放在.stderr文件中2.执行成功 把执行结果放在.stdout文件中这里加一个可扩展的模块,比如,对于用户,可能会自己添加测试用例,那么他输入的用例需要放到.stdin文件中*/// 这里执行对应的文件,我们不关心他运行后的结果,只关心它运行成功或失败umask(0);int _stdin_fd = open(PathUtil::Stdin(filename).c_str(), O_CREAT | O_WRONLY, 0644);int _stdout_fd = open(PathUtil::Stdout(filename).c_str(), O_CREAT | O_WRONLY, 0644);int _stderr_fd = open(PathUtil::Stderr(filename).c_str(), O_CREAT | O_WRONLY, 0644);// 必须得先保证能打开这些文件,如果打不开,后续动作将没有意义(读取不了文件,对比不了结果)if (_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0){LOG(FATAL) << "打开文件失败" << "\n";return -1; // 代表打开文件失败}pid_t pid = fork();if (pid < 0){::close(_stdin_fd);::close(_stdout_fd);::close(_stderr_fd);LOG(FATAL) << " 内部错误,创建子进程错误" << "\n";return -2; // 代码创建子进程失败}else if (pid == 0){// 重定向dup2(_stdin_fd, 0);dup2(_stdout_fd, 1);dup2(_stderr_fd, 2);// 设置限制SetRlimit(cpu_limit, mem_limit); // 这里出问题也是使用信号execl(PathUtil::Exe(filename).c_str() /*文件路径*/, PathUtil::Exe(filename).c_str() /*执行文件*/, nullptr);exit(1);}else{::close(_stdin_fd);::close(_stdout_fd);::close(_stderr_fd);int status = 0;waitpid(pid, &status, 0);return status & 0x7f; // 信号 ,0表示运行成功;-1表示内部错误,其他就是对应的信号值}return -3; // 执行错误}};
}

4.3、第三个功能: Compile_Run 编译并运行

图解:

compile_run.hpp文件:

#pragma once#include <jsoncpp/json/json.h>#include "compiler.hpp"
#include "runner.hpp"#include "../comm/utils.hpp"namespace ns_complie_run
{using namespace ns_compiler;using namespace ns_runner;using namespace ns_utils;class Compile_And_Run{public:static std::string StatusToDesc(int status, const std::string &filename){std::string desc;switch (status){case 0:desc = "编译运行成功!";break;case -1:desc = "代码文件为空";break;case -2:desc = "未知错误";break;case -3:FileUtil::ReadFile(PathUtil::Compile_err(filename), &desc, true); // 读取编译错误的文件内容break;case SIGABRT:desc = "申请空间超过限制"; // 6break;case SIGXCPU:desc = "CPU使用时间超过限制"; // 24break;case SIGFPE:desc = "浮点数溢出"; // 8break;case SIGSEGV:desc = "段错误"; // 11break;default:desc = "未知: " + std::to_string(status);break;}return desc;}static void RemoveTmpFile(const std::string &filename){std::string _src = PathUtil::Src(filename);if (FileUtil::IsExistFile(_src)){unlink(_src.c_str());}std::string _stdin = PathUtil::Stdin(filename);if (FileUtil::IsExistFile(_stdin)){unlink(_stdin.c_str());}std::string _stdout = PathUtil::Stdout(filename);if (FileUtil::IsExistFile(_stdout)){unlink(_stdout.c_str());}std::string _stderr = PathUtil::Stderr(filename);if (FileUtil::IsExistFile(_stderr)){unlink(_stderr.c_str());}std::string _compile_err = PathUtil::Compile_err(filename);if (FileUtil::IsExistFile(_compile_err)){unlink(_compile_err.c_str());}std::string _exe = PathUtil::Exe(filename);if (FileUtil::IsExistFile(_exe)){unlink(_exe.c_str());}}public:/*输入:code: 用户提交的代码input: 用户自己提交的用例in_json = {"code":"#include ....","input":"...","cpu_limit":"...","mem_limit":"..."}输出:status: 状态码reason: 错误原因下面两个选择填入stdout: 程序运行完的结果stderr: 程序异常的原因out_json = {"status":"...","reason":"...","stdout":"...","stderr":"..."}*/// 这里的in_json是网络传输过来static void Compile_Run(std::string &in_json, std::string *out_json){Json::Value in_value;Json::Reader reader;reader.parse(in_json, in_value);std::string code = in_value["code"].asCString();std::string input = in_value["input"].asCString();int cpu_limit = in_value["cpu_limit"].asInt();int mem_limit = in_value["mem_limit"].asInt();int status = 0;Json::Value out_value;std::string filename;int run_code;if (code.size() == 0){// TODOstatus = -1; // 代码文件为空goto END;}// 形成的文件得具有唯一性filename = FileUtil::UniqueFileName(code);// 形成临时src文件// 把code里面的内容放到Src文件中if (!FileUtil::WriteFile(PathUtil::Src(filename), code)){status = -2; // 写文件失败goto END;}// 编译if (!Compiler::Compile(filename)){status = -3; // 编译失败goto END;}// 执行run_code = Runner::Run(filename, cpu_limit, mem_limit);status = run_code;if (run_code < 0){status = -2; // 未知错误}else // run_code >= 0{// 运行成功就是0status = run_code;}END:out_value["status"] = status;out_value["reason"] = StatusToDesc(status, filename);if (status == 0){// 整个过程全部成功// 运行结果在stdout文件std::string _stdout;FileUtil::ReadFile(PathUtil::Stdout(filename), &_stdout, true); // 需要\nout_value["stdout"] = _stdout;// 运行错误在stderr文件 -- 存在一点问题,运行错误不会进来这个文件std::string _stderr;FileUtil::ReadFile(PathUtil::Stderr(filename), &_stderr, true); // 需要\nout_value["stderr"] = _stderr;}// 序列化Json::StyledWriter witer;*out_json = witer.write(out_value);// 已经得到结果了,直接移除临时文件RemoveTmpFile(filename);}};
}

测试功能:(complile_server.cc文件)

#include <jsoncpp/json/json.h>#include "compile_run.hpp"using namespace ns_complie_run;int main()
{// 调试// 网络上发来的Json数据// n_json = {"code":"#include ....","input":"...","cpu_limit":"...","mem_limit":"..."}// out_json = {"status" : "...", "reason" : "...", "stdout" : "...", "stderr" : "..."}std::string in_json;Json::Value in_value;// R"()"  -- raw string ,不加工的数据in_value["code"] = R"(#include <iostream>int main(){// aaaa// int a = 1;// a /= 0;// int *p = new int[1024*1024*50];std::cout << "你好哇"<< std::endl;return 0;})";in_value["input"] = "";in_value["cpu_limit"] = 1;in_value["mem_limit"] = 10240*3;Json::StyledWriter writer;in_json = writer.write(in_value);std::string out_json;Compile_And_Run::Compile_Run(in_json, &out_json);std::cout << out_json << std::endl;return 0;
}

4.4、第四个功能:把 compile_run 打包成网络服务

引入 cpp-httplib 工具:直接添加 httplib.h 库,可以直接使用其功能。

#include <jsoncpp/json/json.h>#include "../comm/httplib.h"
#include "compile_run.hpp"using namespace ns_complie_run;
using namespace httplib;int main(int argc, char *argv[])
{if (argc != 2){std::cout << "Usage : \n        ./compile_server port" << std::endl;return 1;}uint16_t serverport = std::stoi(argv[1]);std::cout << serverport << std::endl;// 把compile_run打包成网络服务Server svr;svr.Get("/hello", [](const Request &req, Response &resp){ resp.set_content("hello httplib,你好啊", "text/plain;charset=utf-8"); });svr.Post("/compile_run", [](const Request &req, Response &resp){std::string in_json = req.body;std::string out_json;if (!in_json.empty()){Compile_And_Run::Compile_Run(in_json, &out_json);resp.set_content(out_json,"application/json;charset=utf-8");} });// 绑定特定端口,多次启动可以绑定多个端口  -- 这里端口记得在服务器开启,不然可能访问不了svr.listen("0.0.0.0", serverport);return 0;
}

五、基于MVC结构的 oj_server 服务设计

MVC结构:

  • M :Model ,通常是和数据交互的模块,比如对题库进行增删查改(文件版/MySQL)。
  • V :View ,通常是拿到数据之后,要进行构建网页,渲染网页内容,展示给用户的(浏览器)。
  • C :Control ,控制器,本质上就是我们的核心业务逻辑。

oj_server 服务设计:本质就是建立一个小型网站。

5.1、第一个功能: 用户请求的服务器路由功能

oj_server.hpp文件:

#include <iostream>#include "../comm/httplib.h"using namespace httplib;int main()
{// 用户请求的服务路由功能Server svr;// 获取题目列表svr.Get("/questions", [](const Request &req, Response &resp){ resp.set_content("获取题目列表", "text/plain;charset=utf-8"); });// 获取指定题目svr.Get(R"(/question/(\d+))", [](const Request &req, Response &resp){ std::string number = req.matches[1];resp.set_content("获取指定题目 : " + number, "text/plain;charset=utf-8"); });// 用户提交代码,使用我们的判题功能 (1.测试用例 2.compile_run)svr.Get(R"(/judge/(\d+))", [](const Request &req, Response &resp) {});svr.listen("0.0.0.0", 8080);return 0;
}

5.2、第二个功能: Model 功能,提供对数据进行操作

oj_model.hpp文件:

#pragma once#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>#include <cassert>
#include <cstdlib>#include "../comm/utils.hpp"
#include "../comm/log.hpp"namespace ns_model
{using namespace ns_log;using namespace ns_utils;struct Question{std::string number; // 题目编号std::string title;  // 题目标题std::string level;  // 题目等级int cpu_limit;      // 时间要求int mem_limit;      // 空间要求std::string desc;   // 题目描述std::string header; // 题目预设给的代码std::string tail;   // 测试用例代码,需要和header拼接再编译运行};const std::string questions_path = "./questions/questions.list"; // 题目列表配置文件const std::string question_path = "./questions/";                // 单个题目的路径class Model{private:// 题号:题目细节std::unordered_map<std::string, Question> _questions;public:Model(){assert(LoadAllQuestion(questions_path));}// 加载配置文件bool LoadAllQuestion(const std::string &question_list){std::ifstream in(question_list);if (!in.is_open()){LOG(FATAL) << "加载题库失败,请检查是否有该题库" << "\n";return false;}std::string line;while (getline(in, line)){std::vector<std::string> tokens;StringUtil::StringSplit(line, &tokens, " ");if (tokens.size() != 5){// 1 回文数 简单 1 30000// 没有读到五个数,有可能是写少了或者多了LOG(WARNING) << "加载单个题目失败,请检查格式" << "\n";continue;}Question q;q.number = tokens[0];q.title = tokens[1];q.level = tokens[2];q.cpu_limit = std::atoi(tokens[3].c_str());q.mem_limit = std::atoi(tokens[4].c_str());FileUtil::ReadFile(question_list + q.number + "desc.txt", &q.desc, true);FileUtil::ReadFile(question_list + q.number + "header.hpp", &q.header, true);FileUtil::ReadFile(question_list + q.number + "tail.hpp", &q.tail, true);_questions.insert({q.number, q});}LOG(INFO) << "加载题库成功!" << "\n";in.close();}// 获取所有题目列表bool GetAllQuestions(std::vector<Question> *out){if (_questions.size() == 0){LOG(ERROR) << "用户获取题库失败" << "\n";return false;}for (const auto &q : _questions){out->push_back(q.second);}return true;}// 获取具体的一道题bool GetOneQuestion(const std::string &number, Question *out){auto iter = _questions.find(number);if (iter == _questions.end()){LOG(ERROR) << "用户获取题目失败,题目编号:" << number << "\n";return false;}*out = iter->second;return true;}~Model() {}};
}

5.3、第三个功能: Control 功能,逻辑控制模块

oj_control.hpp文件:

#pragma once#include <iostream>
#include <jsoncpp/json/json.h>
#include <vector>
#include <mutex>
#include <unistd.h>
#include <fstream>
#include <cassert>
#include <algorithm>// #include "oj_model.hpp"
#include "oj_model_MySQL.hpp"
#include "oj_view.hpp"
#include "../comm/log.hpp"
#include "../comm/utils.hpp"
#include "../comm/httplib.h"namespace ns_control
{using namespace ns_model;using namespace ns_view;using namespace ns_log;using namespace ns_utils;using namespace httplib;// 提供编译运行服务的主机class Machine{public:std::string _ip;uint16_t _port;uint64_t _load;   // 负载量std::mutex *_mtx; // 对_load进行加锁public:Machine() : _ip(""), _port(0), _load(0), _mtx(nullptr) {}// 增加负载void IncLoad(){if (_mtx)_mtx->lock();++_load;if (_mtx)_mtx->unlock();}// 降低负载void DecLoad(){if (_mtx)_mtx->lock();--_load;if (_mtx)_mtx->unlock();}// 读取load,没有太大意义,只是为了统一接口uint64_t Load(){uint64_t load = 0;if (_mtx)_mtx->lock();load = _load;if (_mtx)_mtx->unlock();return load;}// 负载清零,一台主机下线了,它的负载得清零void ResetLoad(){if (_mtx)_mtx->lock();_load = 0;if (_mtx)_mtx->unlock();}~Machine() {}};const std::string machine_conf = "./conf/machine_list.conf";// 提供负载均衡选择主机class LoadBalance{private:std::vector<Machine> _machines; // 提供负载均衡的主机群 -- 以下表在标识每一台主机std::vector<int> _online;       // 在线的主机std::vector<int> _offline;      // 离线的主机std::mutex _mtx;                // 对负载均衡选择加锁,因为可能同时到来多个用户提交代码,而_machines等是临界资源(共享的)public:LoadBalance(){assert(LoadMachine(machine_conf));LOG(INFO) << "加载配置文件 " << machine_conf << " 成功" << "\n";}bool LoadMachine(const std::string &machine_file){std::ifstream in(machine_file);if (!in.is_open()){LOG(FATAL) << "加载配置文件 " << machine_file << " 失败" << "\n";return false;}std::string line;while (getline(in, line)){std::vector<std::string> tokens;StringUtil::StringSplit(line, &tokens, ":");if (tokens.size() != 2){LOG(WARNING) << "加载配置行 " << line << " 失败" << "\n";continue;}Machine m;m._ip = tokens[0];m._port = std::atoi(tokens[1].c_str());m._load = 0;m._mtx = new std::mutex();_online.push_back(_machines.size()); // 先把上线的主机的下标对应主机 0 <-> 第一台 ..._machines.push_back(m);}return true;}// 智能选择负载最小的主机bool SmartChioce(int *id, Machine **m){_mtx.lock();// 负载均衡的算法// 1.随机+hash// 2.轮询+hashint num_online = _online.size(); // 在线的主机if (num_online == 0){_mtx.unlock(); // 出去前先解锁LOG(FATAL) << "没有在线的主机,赶紧检查!" << "\n";return false;}uint64_t min_load = _machines[_online[0]].Load();*id = _online[0];*m = &_machines[_online[0]];for (int i = 1; i < num_online; ++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 OnlineMachine(){_mtx.lock();// 把离线主机添加到在线主机_online.insert(_online.end(), _offline.begin(), _offline.end());_offline.erase(_offline.begin(), _offline.end());_mtx.unlock();LOG(INFO) << "所有主机上线啦!" << "\n";}// 让当前主机进入下线状态void OfflineMachine(int id){_mtx.lock();for (auto iter = _online.begin(); iter != _online.end(); ++iter){if (*iter == id){_machines[id].ResetLoad(); // 清空当前主机的负载_online.erase(iter);_offline.push_back(id);break; // id唯一}}_mtx.unlock();}void ShowMachines(){std::cout << "当前在线的主机 : ";for (auto id : _online){std::cout << id << " ";}std::cout << std::endl;std::cout << "当前离线线的主机 : ";for (auto id : _offline){std::cout << id << " ";}std::cout << std::endl;}~LoadBalance() {}};class Control{private:Model _model;              // 提供后台数据View _view;                // 提供html渲染LoadBalance _load_balance; // 提供核心负载均衡器public:Control() {}void RecoveryMachines(){_load_balance.OnlineMachine();}bool AllQuestions(std::string *html){bool ret = true;// 获取所有题目std::vector<Question> all;if (_model.GetAllQuestions(&all)){// 渲染网页// 排序sort(all.begin(), all.end(), [](const Question &q1, const Question &q2){ return std::atoi(q1.number.c_str()) < std::atoi(q2.number.c_str()); });_view.DrawAllQuestions(all, html);}else{*html = "加载题库失败";ret = false;}return ret;}bool OneQuestion(const std::string &number, std::string *html){bool ret = true;Question q;if (_model.GetOneQuestion(number, &q)){// 渲染网页_view.DrawOneQuestion(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.根据题目编号,拿到题目细节Question q;_model.GetOneQuestion(number, &q);// 1.对in_json进行反序列化,得到code,inputJson::Value in_value;Json::Reader reader;reader.parse(in_json, in_value);std::string code = in_value["code"].asCString();std::string input = in_value["input"].asCString();// 2.拼接代码,header+tailJson::Value compile_run_value;compile_run_value["code"] = code + "\n" + q.tail; // 记得加\n,不然可能存在编译的时候和ifndef COMPILE 共一行compile_run_value["input"] = input;compile_run_value["cpu_limit"] = q.cpu_limit;compile_run_value["mem_limit"] = q.mem_limit;Json::StyledWriter writer;std::string compile_run_string = writer.write(compile_run_value);// 3.选择负载最低的主机// 规则:一直选择,直到主机可用,否则就是主机已经全部挂掉while (true){int id = 0;Machine *m = nullptr;if (!_load_balance.SmartChioce(&id, &m)){break; // 主机全部挂掉了}// 4.发起http请求,得到结果Client cli(m->_ip, m->_port);m->IncLoad(); // 用的时候增加负载值LOG(INFO) << "连接主机成功 , 主机id : " << id << " 详情 : " << m->_ip << ":" << m->_port << " 负载 : " << m->Load() << "\n";// inline Result Client::Post(const char *path, const std::string &body, const char *content_type)auto res = cli.Post("/compile_run", compile_run_string, "application/json;charset=utf-8");if (res) // 编译运行{if (res->status == 200){// 请求成功// 5.将结果给out_json*out_json = res->body;m->DecLoad(); // 用完后减少负载值break;}m->DecLoad();}else{// 请求失败LOG(WARNING) << "当前请求的主机id : " << id << " 详情 : " << m->_ip << ":" << m->_port << " 可能已经离线" << "\n";_load_balance.OfflineMachine(id); // 当前主机下线_load_balance.ShowMachines();     // 用来测试}sleep(1);}}~Control() {}};
}

5.4、第四个功能:View 功能,页面渲染模块

oj_view.hpp文件了:

#pragma once#include <ctemplate/template.h>#include <string>
#include <vector>#include "oj_model.hpp"namespace ns_view
{using namespace ns_model;const std::string draw_path = "./template_html/";class View{public:void DrawAllQuestions(std::vector<Question> &all, std::string *html){std::string in_html = draw_path + "all_questions.html";// 形成数据字典ctemplate::TemplateDictionary root("all");for (const auto &q : all){// 形成子数据字典ctemplate::TemplateDictionary *sub = root.AddSectionDictionary("question_list");sub->SetValue("number", q.number);sub->SetValue("title", q.title);sub->SetValue("level", q.level);}// 获取被渲染的网页对象ctemplate::Template *tpl = ctemplate::Template::GetTemplate(in_html, ctemplate::DO_NOT_STRIP);// 添加字典数据到网页中tpl->Expand(html, &root);}void DrawOneQuestion(const Question &q, std::string *html){std::string in_html = draw_path + "question.html";ctemplate::TemplateDictionary root("one");root.SetValue("number", q.number);root.SetValue("title", q.title);root.SetValue("level", q.level);root.SetValue("desc", q.desc);root.SetValue("pre_code", q.header);ctemplate::Template *tpl = ctemplate::Template::GetTemplate(in_html, ctemplate::DO_NOT_STRIP);tpl->Expand(html, &root);}};
}

六、基于 MySQL 版的 Model 功能

oj_model_MySQL.hpp文件:

#pragma once#include <string>
#include <vector>
#include <fstream>
#include <unordered_map>#include <cassert>
#include <cstdlib>#include <mysql/mysql.h>#include "../comm/utils.hpp"
#include "../comm/log.hpp"namespace ns_model
{using namespace ns_log;using namespace ns_utils;struct Question{std::string number; // 题目编号 -- 唯一std::string title;  // 题目标题std::string level;  // 题目难度int cpu_limit;      // 时间要求int mem_limit;      // 空间要求std::string desc;   // 题目描述std::string header; // 题目预设给的代码std::string tail;   // 测试用例代码,需要和header拼接再编译运行};const std::string questions_table = "oj_questions"; // 数据库表名const std::string host = "127.0.0.1";const uint16_t port = 3306;const std::string passwd = "xp1014647664";const std::string db = "oj";const std::string user = "oj_client";class Model{private:bool MySQLQuery(const std::string &sql, std::vector<Question> *out){// 创建句柄MYSQL *my = mysql_init(nullptr);// 连接数据库if (nullptr == mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0)){LOG(FATAL) << "连接数据库失败!" << "\n";mysql_close(my); // 释放句柄return false;}LOG(INFO) << "连接数据库成功!" << "\n";mysql_set_character_set(my, "utf8");// 执行语句if (0 != mysql_query(my, sql.c_str())){LOG(WARNING) << "执行语句 " << sql << " 失败!" << "\n";mysql_close(my); // 释放句柄return false;}// 提取结果MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){LOG(WARNING) << "查询未返回结果集" << "\n";mysql_close(my);return false;}// 获取行列数int rows = mysql_num_rows(res);// int cols = mysql_num_fields(res);for (int i = 0; i < rows; ++i){// 获取每一行Question q;MYSQL_ROW row = mysql_fetch_row(res);q.number = row[0];q.title = row[1];q.level = row[2];q.cpu_limit = atoi(row[3]);q.mem_limit = atoi(row[4]);q.desc = row[5];q.header = row[6];q.tail = row[7];out->push_back(q);}mysql_free_result(res);mysql_close(my);return true;}public:Model() {}// 获取所有题目列表bool GetAllQuestions(std::vector<Question> *out){std::string sql = "select * from ";sql += questions_table;return MySQLQuery(sql, out);}// 获取具体的一道题bool GetOneQuestion(const std::string &number, Question *out){bool ret = false;std::string sql = "select * from ";sql += questions_table;sql += " where number=";sql += number;std::vector<Question> result;if (MySQLQuery(sql, &result)){if (result.size() == 1){*out = result[0];return true;}}return ret;}~Model() {}};
}

七、前端网页设计 html

主页:index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>这是我的个人OJ系统</title><style>/* 消除默认样式 */* {margin: 0;padding: 0;box-sizing: border-box;}html,body {width: 100%;height: 100%;font-family: 'Arial', sans-serif;background: linear-gradient(to bottom, #f0f4f8, #d9e2ec);}.container {min-height: 100vh;display: flex;flex-direction: column;}/* 导航栏样式 */.navbar {display: flex;justify-content: space-between;align-items: center;background-color: #333;padding: 0 20px;height: 60px;}/* 左侧导航菜单 */.navbar-left {display: flex;}.navbar-left a {color: white;font-size: 18px;text-decoration: none;padding: 14px 20px;transition: background-color 0.3s ease;}.navbar-left a:hover {background-color: #4CAF50;border-radius: 4px;}/* 右侧登录按钮 */.navbar-right .login {color: white;font-size: 18px;text-decoration: none;padding: 14px 20px;background-color: #4CAF50;border-radius: 4px;transition: background-color 0.3s ease;}.navbar-right .login:hover {background-color: #45a049;}/* 内容样式 */.content {flex: 1;display: flex;flex-direction: column;justify-content: center;align-items: center;text-align: center;padding: 20px;}.content h1 {font-size: 36px;color: #333;margin-bottom: 20px;letter-spacing: 2px;}.content p {font-size: 20px;color: #666;margin-bottom: 30px;}.content a {font-size: 22px;padding: 10px 25px;color: white;background-color: #4CAF50;border-radius: 5px;text-decoration: none;transition: background-color 0.3s ease;}.content a:hover {background-color: #45a049;}</style>
</head><body><div class="container"><!-- 导航栏 --><div class="navbar"><!-- 左侧导航链接 --><div class="navbar-left"><a href="/">首页</a><a href="/all_questions">题库</a><a href="#">竞赛</a><a href="#">讨论</a><a href="#">求职</a></div><!-- 右侧登录按钮 --><div class="navbar-right"><a class="login" href="#">登录</a></div></div><!-- 内容区 --><div class="content"><h1>欢迎来到我的Online Judge平台</h1><p>这是一个我个人独立开发的在线OJ平台,享受编程的乐趣吧!</p><a href="/all_questions">点击我开始编程啦!</a></div></div>
</body></html>

题目列表页:all_questions.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>在线OJ-题目列表</title><style>/* 清除默认样式 */* {margin: 0;padding: 0;box-sizing: border-box;}body,html {font-family: 'Arial', sans-serif;height: 100%;background: linear-gradient(to right, #eef2f3, #8e9eab);}.container {display: flex;flex-direction: column;min-height: 100vh;}/* 导航栏样式 */.navbar {display: flex;justify-content: space-between;align-items: center;background-color: #333;padding: 0 20px;height: 60px;}/* 左侧导航菜单 */.navbar-left {display: flex;}.navbar-left a {color: white;font-size: 18px;text-decoration: none;padding: 14px 20px;transition: background-color 0.3s ease;}.navbar-left a:hover {background-color: #4CAF50;border-radius: 4px;}/* 右侧登录按钮 */.navbar-right .login {color: white;font-size: 18px;text-decoration: none;padding: 14px 20px;background-color: #4CAF50;border-radius: 4px;transition: background-color 0.3s ease;}.navbar-right .login:hover {background-color: #45a049;}/* 题目列表 */.question_list {flex: 1;padding: 50px 20px;display: flex;flex-direction: column;align-items: center;}.question_list h1 {font-size: 36px;color: #333;margin-bottom: 30px;}/* 表格样式 */table {width: 80%;border-collapse: collapse;background-color: white;box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);border-radius: 10px;overflow: hidden;}th,td {padding: 15px;text-align: center;border-bottom: 1px solid #ddd;}th {background-color: #4CAF50;color: white;font-size: 18px;}tr:hover {background-color: #f2f2f2;}.item a {color: #333;text-decoration: none;transition: color 0.3s ease, text-decoration 0.3s ease;}.item a:hover {color: #4CAF50;text-decoration: underline;}/* 页脚 */.footer {height: 50px;background-color: #333;color: white;display: flex;justify-content: center;align-items: center;}.footer h4 {margin: 0;}</style>
</head><body><div class="container"><!-- 导航栏 --><div class="navbar"><!-- 左侧导航链接 --><div class="navbar-left"><a href="/">首页</a><a href="/all_questions">题库</a><a href="#">竞赛</a><a href="#">讨论</a><a href="#">求职</a></div><!-- 右侧登录按钮 --><div class="navbar-right"><a class="login" href="#">登录</a></div></div><!-- 题目列表 --><div class="question_list"><h1>OnlineJudge 题目列表</h1><table><tr><th class="item">编号</th><th class="item">标题</th><th class="item">难度</th></tr>{{#question_list}}<tr><td class="item">{{number}}</td><td class="item"><a href="/question/{{number}}">{{title}}</a></td><td class="item">{{level}}</td></tr>{{/question_list}}</table></div><!-- 页脚 --><div class="footer"><h4>@Xpccccc</h4></div></div>
</body></html>

指定题目页:question.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>{{number}}.{{title}}</title><!-- 引入ACE插件 --><script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ace.js" type="text/javascript" charset="utf-8"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.6/ext-language_tools.js" type="text/javascript" charset="utf-8"></script><script src="http://code.jquery.com/jquery-2.1.1.min.js"></script><style>/* 清除默认样式 */* {margin: 0;padding: 0;box-sizing: border-box;}html,body {width: 100%;height: 100%;font-family: 'Arial', sans-serif;background-color: #f4f4f4;}.container {display: flex;flex-direction: column;min-height: 100vh;}/* 导航栏样式 */.navbar {display: flex;justify-content: space-between;align-items: center;background-color: #333;padding: 0 20px;height: 60px;}/* 左侧导航菜单 */.navbar-left {display: flex;}.navbar-left a {color: white;font-size: 18px;text-decoration: none;padding: 14px 20px;transition: background-color 0.3s ease;}.navbar-left a:hover {background-color: #4CAF50;border-radius: 4px;}/* 右侧登录按钮 */.navbar-right .login {color: white;font-size: 18px;text-decoration: none;padding: 14px 20px;background-color: #4CAF50;border-radius: 4px;transition: background-color 0.3s ease;}.navbar-right .login:hover {background-color: #45a049;}/* 左右呈现,题目描述和预设代码 */.part1 {display: flex;flex: 1;margin-top: 10px;padding: 20px;}.left_desc {flex: 1;padding: 20px;background-color: #ecf0f1;overflow-y: auto;}.left_desc h3 {font-size: 22px;color: #2c3e50;margin-bottom: 15px;}.left_desc pre {font-size: 16px;line-height: 1.5;white-space: pre-wrap;}.right_code {flex: 1;padding: 20px;background-color: #ecf0f1;}.right_code .ace_editor {width: 100%;height: 100%;border: 1px solid #bdc3c7;border-radius: 4px;}/* 提交代码和显示结果 */.part2 {display: flex;justify-content: space-between;align-items: center;padding: 15px 20px;background-color: #f4f4f4;}.result {flex: 1;margin-right: 20px;}.btn-submit {width: 120px;height: 50px;background-color: #26bb9c;color: white;border: none;border-radius: 5px;font-size: 18px;cursor: pointer;transition: background-color 0.3s;}.btn-submit:hover {background-color: #1abc9c;}.result pre {font-size: 16px;margin-top: 10px;white-space: pre-wrap;word-wrap: break-word;}</style>
</head><body><div class="container"><!-- 导航栏 --><div class="navbar"><!-- 左侧导航链接 --><div class="navbar-left"><a href="/">首页</a><a href="/all_questions">题库</a><a href="#">竞赛</a><a href="#">讨论</a><a href="#">求职</a></div><!-- 右侧登录按钮 --><div class="navbar-right"><a class="login" href="#">登录</a></div></div><!-- 左右呈现,题目描述和预设代码 --><div class="part1"><div class="left_desc"><h3><span id="number">{{number}}</span>. {{title}} - {{level}}</h3><pre>{{desc}}</pre></div><div class="right_code"><pre id="code" class="ace_editor"><textarea class="ace_text-input">{{pre_code}}</textarea></pre></div></div><!-- 提交并显示结果 --><div class="part2"><div class="result"></div><button class="btn-submit" onclick="submit()">提交代码</button></div></div><script>var editor = ace.edit("code");editor.setTheme("ace/theme/monokai");editor.session.setMode("ace/mode/c_cpp");editor.setFontSize(16);editor.getSession().setTabSize(4);editor.setReadOnly(false);ace.require("ace/ext/language_tools");editor.setOptions({enableBasicAutocompletion: true,enableSnippets: true,enableLiveAutocompletion: true});function submit() {var code = editor.getSession().getValue();var number = $("#number").text();var judge_url = "/judge/" + number;$.ajax({method: 'POST',url: judge_url,dataType: 'json',contentType: 'application/json;charset=utf-8',data: JSON.stringify({'code': code,'input': ''}),success: function (data) {show_result(data);}});function show_result(data) {var result_div = $(".container .part2 .result");result_div.empty();var reason_label = $("<p>", {text: data.reason});reason_label.appendTo(result_div);if (data.status == 0) {var stdout_label = $("<pre>", {text: data.stdout});var stderr_label = $("<pre>", {text: data.stderr});stdout_label.appendTo(result_div);stderr_label.appendTo(result_div);}}}</script>
</body></html>

八、发布项目(Makefile)

使用顶层Makefile来操作所有能用到的make命令:

.PHONY:all
all:@cd compile_server;\make;\cd -;\cd oj_server;\make;\cd -;.PHONY:output
output:@mkdir -p output/compile_server;\mkdir -p output/oj_server;\cp -rf compile_server/compile_server output/compile_server;\cp -rf compile_server/tmp output/compile_server;\cp -rf oj_server/conf output/oj_server;\cp -rf oj_server/questions output/oj_server;\cp -rf oj_server/template_html output/oj_server;\cp -rf oj_server/wwwroot output/oj_server;\cp -rf oj_server/oj_server output/oj_server;\.PHONY:clean
clean:@cd compile_server;\make clean;\cd -;\cd oj_server;\make clean;\cd -;rm -rf output

九、项目整体效果演示

十、结束语 

今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。

​​​ 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com