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

实战项目负载均衡式在线 OJ

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

> 目标:能自己实现负载均衡式在线 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

九、项目整体效果演示

十、结束语 

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

​​​ 

相关文章:

实战项目负载均衡式在线 OJ

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;能自己实现负载均衡式在线 OJ。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! > 专栏选自&#xff1…...

shell脚本启动springboot项目

nohup java -jar springboot.jar > springboot.log 2>&1 & 表示日志输出重定向到springboot.log日志文件, 而原本的日志继续输出到 项目同级的log文件夹下, 所以这个重定向没必要. 我们没必要要2分日志 #!/bin/bash# 获取springboot项目的进程ID PID$(ps -e…...

Django 自定义路由转换器

步骤 创建自定义转换器类 必须定义 regex 属性&#xff08;用于匹配参数&#xff09;。必须实现 to_python 和 to_url 方法。 to_python: 将匹配的参数转换为视图函数可用的 Python 数据。to_url: 将数据转换为 URL 格式&#xff08;用于反向解析&#xff09;。 注册转换器 使用…...

低速接口项目之串口Uart开发(二)——FIFO实现串口数据的收发回环测试

本节目录 一、设计思路 二、loop环回模块 三、仿真模块 四、仿真验证 五、上板验证 六、往期文章链接本节内容 一、设计思路 串口数据的收发回环测试&#xff0c;最简单的硬件测试是把Tx和Rx连接在一起&#xff0c;然后上位机进行发送和接收测试&#xff0c;但是需要考虑到串…...

Oracle 数据库 IDENTITY 列

IDENTITY列是Oracle数据库12c推出的新特性。之所以叫IDENTITY列&#xff0c;是由于其支持ANSI SQL 关键字 IDENTITY&#xff0c;其内部实现还是使用SEQUENCE。 不过推出这个新语法也是应该的&#xff0c;毕竟MyQL已经有 AUTO_INCREMENT列&#xff0c;而SQL Server也已经有IDENT…...

算法笔记:单调队列

单调队列&#xff1a; 队列元素之间的关系具有单调性&#xff08;从队首到队尾单调递增/递减&#xff09;&#xff0c;队首和队尾都可以进行入队出队&#xff08;即插入删除&#xff09;操作&#xff0c;本质是由双端队列deque实现。 适用问题 通常解决动态小区间中寻找极值…...

深度解析:Nginx模块架构与工作机制的奥秘

文章目录 前言Nginx是什么?Ngnix特点&#xff1a; 一、Nginx模块与工作原理1.Nginx的模块1.1 Nginx模块常规的HTTP请求和响应的流程图:1.2 Nginx的模块从结构上分为如下三类&#xff1a;1.3 Nginx的模块从功能上分为如下三类: 2.Nginx的进程模型2.1 Nginx进程结构2.2 nginx进程…...

【git】commit之后,想撤销commit

一、已经commit&#xff0c;想要回退到上一步 保留代码 git reset --soft HEAD^回退到具体的哪一步 HEAD^的意思是上一个版本&#xff0c;也可以写成HEAD~1如果你进行了2次commit&#xff0c;想都撤回&#xff0c;可以使用HEAD~2二、git reflog 查看 sha值 git reflog 回到…...

实现在两台宿主机下的docker container 中实现多机器通讯

基于我的实验背景 上位机&#xff1a;ubuntu 20.04 (docker humble 22.04) 下位机&#xff1a;ubuntu 22.04&#xff08;docker noetic 20.04&#xff09; 目标&#xff1a;实现在上位机中的docker container 容器的22.04环境去成功远程访问 非同网段的下位机的20.04的contai…...

Python 网络爬虫操作指南

网络爬虫是自动化获取互联网上信息的一种工具。它广泛应用于数据采集、分析以及实现信息聚合等众多领域。本文将为你提供一个完整的Python网络爬虫操作指南&#xff0c;帮助你从零开始学习并实现简单的网络爬虫。我们将涵盖基本的爬虫概念、Python环境配置、常用库介绍。 上传…...

vue3 uniapp 扫普通链接或二维码打开小程序并获取携带参数

vue3 uniapp 扫普通链接或二维码打开小程序并获取携带参数 微信公众平台添加配置 微信公众平台 > 开发管理 > 开发设置 > 扫普通链接二维码打开小程序 配置链接规则需要下载校验文档给后端存入服务器中&#xff0c;保存配置的时候会校验一次&#xff0c;确定当前的配…...

高标准农田智慧农业系统建设方案

1 项目概述 1.1 建设背景 我国是农业大国,近30年来农田高产量主要依靠农药化肥的大量投入,大部分化肥和水资源没有被有效利用而随地弃置,导致大量养分损失并造成环境污染。我国农业生产仍然以传统生产模式为主,传统耕种只能凭经验施肥灌溉,不仅浪费大量的人力物力,也对环…...

【python图解】数据结构之字典和集合

【python图解】数据结构之字典和集合 在 Python 中&#xff0c;字典和集合是另外的两种重要数据结构&#xff0c;它们分别用于存储键值对和无序的唯一元素集合。下面我们将详细介绍字典和集合的定义、操作方法、使用场景及相关案例。 1. 字典&#xff08;Dictionary&#xff0…...

vue 预览pdf 【@sunsetglow/vue-pdf-viewer】开箱即用,无需开发

sunsetglow/vue-pdf-viewer 开箱即用的pdf插件sunsetglow/vue-pdf-viewer, vue3 版本 无需多余开发&#xff0c;操作简单&#xff0c;支持大文件 pdf 滚动加载&#xff0c;缩放&#xff0c;左侧导航&#xff0c;下载&#xff0c;页码&#xff0c;打印&#xff0c;文本复制&…...

TCL嵌入式面试题及参考答案

USB2.0 和 USB3.0 的速度区别是什么? USB2.0 和 USB3.0 在速度上有显著的区别。USB2.0 理论上的最高传输速度为 480Mbps,也就是 60MB/s 左右。而 USB3.0 的理论传输速度则可达到 5Gbps,即约 625MB/s ,这比 USB2.0 快了很多倍。 在实际应用中,USB2.0 通常用于一些对速度要求…...

即时通讯服务器被ddos攻击了怎么办?

攻击即时通讯系统的主要手段 击键记录 目前盗取即时通讯工具帐号信息的最主要方法是通过特洛伊木马等恶意软件&#xff0c;例如QQ木马&#xff0c;这类程序能够盗取QQ密码信息&#xff0c;常见的能够盗取最新版本QQ密码的木马程序有十几种之多。几乎所有主要的QQ木马程序都采…...

RTC QoS方法十三.(ReedSolomonFEC简介)

一、FlexFEC恢复的困局 在使用FlexFEC进行冗余的时候&#xff0c;经验值需要冗余5倍的丢包率&#xff0c;才能有比较高的恢复率。 Flex FEC在2D数组异或时能获得比较高的恢复率&#xff0c;但是如上图所示&#xff0c;25个包发送10个FEC包&#xff0c;成本为10/2540%的冗余度。…...

嵌入式开发工程师面试题 - 2024/11/24

原文嵌入式开发工程师面试题 - 2024/11/24 转载请注明来源 1.若有以下定义语句double a[8]&#xff0c;*pa&#xff1b;int i5&#xff1b;对数组元素错误的引用是&#xff1f; A *a B a[5] C *&#xff08;p1&#xff09; D p[8] 解析&#xff1a; 在 C 或 C 语言中&am…...

《Shader入门精要》透明效果

代码以及实例图可以看github &#xff1a;zaizai77/Shader-Learn: 实现一些书里讲到的shader 在实时渲染中要实现透明效果&#xff0c;通常会在渲染模型时控制它的透明通道&#xff08;Alpha Channel&#xff09;​。当开启透明混合后&#xff0c;当一个物体被渲染到屏幕上时&…...

一篇文章了解Linux

目录 一&#xff1a;命令 1 ls命令作用 2 目录切换命令&#xff08;cd/pwd&#xff09; &#xff08;1)cd切换工作目录命令 3 相对路径、绝对路径和特殊路径 (1)相对路径和绝对路径的概念和写法 (2)几种特殊路径的表示符 (3)练习题&#xff1a; 4 创建目录命令&#x…...

创建字典时,键名不能是数字

4. 请问下面创建字典的 8 种方法中&#xff0c;哪几种是正确的。 >>> a {99:"吕布", 90:"关羽", 60:"刘备"}>>> b dict(99:"吕布", 90:"关羽", 60:"刘备")>>> c dict(99"吕布…...

架构-微服务-环境搭建

文章目录 前言一、案例准备1. 技术选型2. 模块设计3. 微服务调用 二、创建父工程三、创建基础模块四、创建用户微服务五、创建商品微服务六、创建订单微服务 前言 ‌微服务环境搭建‌ 使用的电商项目中的商品、订单、用户为案例进行讲解。 一、案例准备 1. 技术选型 maven&a…...

mysql根据日期查询没有的日期也要显示数据

先查询出日期数据(当前日期往前推12个月) select bb.datefrom (select num : num 1,date_format(adddate(date_sub(date_sub(curdate(),interval 12 month),interval 1 month),interval num month), %Y-%m) as datefrom mysql.help_topic,(select num : 0) as twhere addd…...

ArcGIS定义投影与投影的区别(数据和底图不套合的原因和解决办法)

今天介绍一下ArcGIS中定义投影与投影的区别。 给大家解惑一下为什么经常出现自己的数据无法和底图套合的情况。 一 目录 1、ArcGIS定义投影与投影的概念区别 2、ArcGIS定义正确的坐标系 3、ArcGIS动态投影实现套合 4、ArcGIS地理坐标系转投影坐标系&#xff08;错误做法&am…...

SQL Server数据库日志(ldf文件)清理

随着系统运行时间的推移&#xff0c;数据库日志文件会变得越来越大&#xff0c;这时我们需要对日志文件进行备份或清理。 下面是日常运维中比较常用的日志清理SQL语句 --- 查询数据库log名称 USE testdb SELECT name, physical_name FROM sys.master_files WHERE database_id …...

ubuntu 安装proxychains

在Ubuntu上安装Proxychains&#xff0c;你可以按照以下步骤操作&#xff1a; 1、更新列表 sudo apt-update 2、安装Proxychains sudo apt-get install proxychains 3、安装完成后&#xff0c;你可以通过编辑/etc/proxychains.conf文件来配置代理规则 以下是一个简单的配置示例&…...

Maven学习笔记

Maven功能介绍 提供了一套标准化的项目结构提供了一套标准化的构建流程&#xff08;编译、测试、打包、发布.....&#xff09;提供了一套依赖管理机制 依赖管理其实就是管理你项目所依赖的第三方资源&#xff08;jar包、插件...&#xff09; ①Maven使用标准的坐标配置来管理…...

.net 支持跨平台(桌面)系列技术汇总

1. 首先微软老大哥的.net core 。 .NET Core 是微软开发的一个跨平台、高性能的开源框架&#xff0c;用于构建云和互联网连接的新型应用。 它允许开发者在 Windows、macOS 和 Linux 上使用喜爱的开发工具进行开发&#xff0c;并支持部署到云或本地环境。 .NET Core 是对 .NET …...

[Golang]传递一个切片(slice)和使用变参(...)语法传递多个参数之间的区别

在 Go 中&#xff0c;传递一个切片&#xff08;slice&#xff09;和使用变参&#xff08;…&#xff09;语法传递多个参数之间有一些关键区别。让我们详细讨论这两种方式之间的区别&#xff1a; 传递切片&#xff08;Slice&#xff09; 传递方式&#xff1a; 传递切片时&…...

【PGCCC】Postgresql BRIN 索引原理

前言 postgresql 提供了块级索引&#xff08;简称 BRIN&#xff09;&#xff0c;主要适用于类似时序数据之类的&#xff0c;有着天然的顺序&#xff0c;而且都是添加写的场景。相比于 btree 索引&#xff0c;它的体积小得多&#xff0c;非常适用于大数据量的场景。 原理 pos…...

安全加固方案

交换机安全加固 查看是否关闭未使用的接口 25GE1/0/1、25GE1/0/47、25GE1/0/48需要使用&#xff0c;暂不关闭 system-view # interface Eth-Trunk99 shutdown quit interface Eth-Trunk100 shutdown quit interface Eth-Trunk110 shutdown quit interface 25GE1/…...

Adobe Illustrator 2024 安装教程与下载分享

介绍一下 下载直接看文章末尾 Adobe Illustrator 是一款由Adobe Systems开发的矢量图形编辑软件。它广泛应用于创建和编辑矢量图形、插图、徽标、图标、排版和广告等领域。以下是Adobe Illustrator的一些主要特点和功能&#xff1a; 矢量绘图&#xff1a;Illustrator使用矢量…...

WSL安装不同版本ubuntu(已有ubuntu20.04,再装ubuntu18.04)

参考&#xff1a; 如何在 WSL 中删除指定版本的 Ubuntu&#xff08;以删除 Ubuntu 22.04 为例&#xff09;_wsl卸载某个-CSDN博客 已有ubuntu20.04&#xff0c;现在再安装一个ubuntu18.04 直接参考下面我写的链接的第四步&#xff0c;前面的步骤都不需要再做了 Win11安装WSL…...

指针测试总结(一)(一维数组)

1.取一维数组的首地址 int main() {int arr[3] {5,8,1}; printf("%d\n",arr);printf("%d\n",&arr);printf("%d\n",&arr[0]);printf("%d\n",&arr0); }输出结果&#xff1a; 1096809108 1096809108 1096809108 1096809108…...

CentOS环境上离线安装python3及相关包

0. 准备操作系统及安装包 准备操作系统环境&#xff1a; 首先安装依赖包&#xff0c;安装相应的编译工具 [rootbigdatahost bin]# yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-d…...

Matlab 深度学习工具箱 案例学习与测试————求二阶微分方程

clc clear% 定义输入变量 x linspace(0,2,10000);% 定义网络的层参数 inputSize 1; layers [featureInputLayer(inputSize,Normalization"none")fullyConnectedLayer(10)sigmoidLayerfullyConnectedLayer(1)sigmoidLayer]; % 创建网络 net dlnetwork(layers);% 训…...

算法笔记:回溯算法

回溯法理论基础 「回溯是递归的副产品&#xff0c;只要有递归就会有回溯」&#xff0c;所以回溯法也经常和二叉树遍历&#xff0c;深度优先搜索混在一起&#xff0c;因为这两种方式都是用了递归。 回溯法就是暴力搜索&#xff0c;并不是什么高效的算法&#xff0c;最多再剪枝一…...

[工具分享] 根据Excel数据根据Word文档模板,批量创建生成Word文档并重命名,方便快速查找打印

前几天交楼的小姐姐要多份Word文档合同打印给客户&#xff0c;那么100份就需要修改100次 上面好多都是模板的制式文件&#xff0c;里面的部分数据都是要根据实际值来变动的&#xff0c; 那么有没有快速的方法来操作呢&#xff0c;还是只能一个个手动的改&#xff0c;又容易出…...

数据库的联合查询

数据库的联合查询 简介为什么要使⽤联合查询多表联合查询时MYSQL内部是如何进⾏计算的构造练习案例数据案例&#xff1a;⼀个完整的联合查询的过程 内连接语法⽰例 外连接语法 ⽰例⾃连接应⽤场景示例表连接练习 ⼦查询语法单⾏⼦查询多⾏⼦查询多列⼦查询在from⼦句中使⽤⼦查…...

DICOM图像深入解析:为何部分DR/CR图像默认显示为反色?

概述 在数字医学影像处理中,CR(Computed Radiography,计算机放射摄影)和DR(Digital Radiography,数字放射摄影)技术广泛应用于医疗影像获取与分析。然而,临床实践中常常遇到这样一个问题:部分CR/DR图像在默认打开时呈现为反色(即负片效果),需手动反色后才能正常阅片…...

C#基础上机练习题

21.计算500-800区间内素数的个数cn&#xff0c;并按所求素数的值从大到小的顺序排列&#xff0c;再计算其间隔加、减之和&#xff0c;即第1个素数-第2个素数第3个素数-第4个素数第5个素数……的值sum。请编写函数实现程序的要求&#xff0c;把结果cn和sum输出。 22.在三位整数…...

UE5 实现组合键触发事件的方法

因为工作原因。 需要用大括号{和}来触发事件 但是在蓝图中搜了一下&#xff0c;发现键盘事件里根本就没有{}这两个键。 花费了一下午&#xff0c;终于找到解决的方法了&#xff0c;也就是增强输入的弦操作 首先创建一个项目 纯蓝图或者C都可行 进入到内容浏览器的默认页面 …...

Linux麦克风录音实战

在 Linux 上使用麦克风进行录音可以通过多种方式实现&#xff0c;包括使用命令行工具、图形界面应用程序以及编程接口。下面我将介绍几种常见的方法&#xff0c;从简单的命令行工具到使用 PortAudio 库进行编程。 一. 使用arecord命令行工具 arecord 是 ALSA&#xff08;Adva…...

Diving into the STM32 HAL-----Timers笔记

嵌入式设备会按时间执行某些活动。对于真正简单且不准确的延迟&#xff0c;繁忙的循环可以执行任务&#xff0c;但是使用 CPU 内核执行与时间相关的活动从来都不是一个聪明的解决方案。因此&#xff0c;所有微控制器都提供专用的硬件外设&#xff1a;定时器。定时器不仅是时基生…...

RPC学习

一、什么是 RPC RPC&#xff08;Remote Procedure Call&#xff09;&#xff0c;即远程过程调用&#xff0c;是一种计算机通信协议&#xff0c;它允许运行在一台计算机上的程序调用另一台计算机上的子程序或函数&#xff0c;就好像调用本地程序中的函数一样&#xff0c;无需程序…...

STM32端口模拟编码器输入

文章目录 前言一、正交编码器是什么&#xff1f;二、使用步骤2.1开启时钟2.2配置编码器引脚 TIM3 CH1(PA6) CH2 (PA7)上拉输入2.3.初始化编码器时基2.4 初始化编码器输入2.5 配置编码器接口2.6 开启定时器2.7获取编码器数据 三、参考程序四、测试结果4.1测试方法4.2串口输出结果…...

深入理解 MyBatis 的缓存机制:一级缓存与二级缓存

MyBatis 是目前 Java 开发中常用的一种 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它不仅简化了 SQL 语句的编写和管理&#xff0c;还提供了强大的缓存机制&#xff0c;用以提高数据库访问的性能。MyBatis 的缓存分为一级缓存和二级缓存&#xff0c;分别应用于不…...

做一个FabricJS.cc的中文文档网站——面向markdown编程

&#x1f4e2;欢迎点赞 &#xff1a;&#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff0c;赐人玫瑰&#xff0c;手留余香&#xff01;&#x1f4e2;本文作者&#xff1a;由webmote 原创&#x1f4e2;作者格言&#xff1a;新的征程&#xff0c;用爱发电&#…...

Elasticsearch在部署时,对Linux的设置有哪些优化方法?

大家好&#xff0c;我是锋哥。今天分享关于【Elasticsearch在部署时&#xff0c;对Linux的设置有哪些优化方法&#xff1f;】面试题。希望对大家有帮助&#xff1b; Elasticsearch在部署时&#xff0c;对Linux的设置有哪些优化方法&#xff1f; 1000道 互联网大厂Java工程师 精…...

ThingsBoard规则链节点:Azure IoT Hub 节点详解

目录 引言 1. Azure IoT Hub 节点简介 2. 节点配置 2.1 基本配置示例 3. 使用场景 3.1 数据传输 3.2 数据分析 3.3 设备管理 4. 实际项目中的应用 4.1 项目背景 4.2 项目需求 4.3 实现步骤 5. 总结 引言 ThingsBoard 是一个开源的物联网平台&#xff0c;提供了设备…...