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

网站seo外包公司有哪些机关网站建设存在的问题

网站seo外包公司有哪些,机关网站建设存在的问题,定制的网站源码,财务软件排名前言#xff1a; 该篇讲述了实现基于负载均衡式的在线oj#xff0c;即类似在线编程做题网站一样#xff0c;文章尽可能详细讲述细节即实现#xff0c;便于大家了解学习。 文章将采用单篇不分段形式#xff08;ps#xff1a;切着麻烦#xff09;#xff0c;附图文#… 前言 该篇讲述了实现基于负载均衡式的在线oj即类似在线编程做题网站一样文章尽可能详细讲述细节即实现便于大家了解学习。 文章将采用单篇不分段形式ps切着麻烦附图文附代码代码部署在云服务器上 技术栈 C STL标准库 Boost 标准库 cpp-httpib 开源库 ctemplate 第三方开源前端网页渲染库 jsoncpp 第三方开源序列化、反序列化库 负载均衡的设计 多进程、多线程 MYSQL C connect Ace前端在线编辑器 html/cdd/js/jquery/ajax 开发环境 vscodemysql workbenchCentos 7云服务器 宏观结构 comm:公共模块compile_sever:编译运行模块oj_server获取题目负载均衡等 项目演示  https://blog.csdn.net/Obto_/article/details/132558642?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132558642%22%2C%22source%22%3A%22Obto_%22%7Dhttps://blog.csdn.net/Obto_/article/details/132558642?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132558642%22%2C%22source%22%3A%22Obto_%22%7D 项目设计 -- 编译服务 工具类的准备 供程序中各个部分调用的方法类 #pragma once#include iostream #include string #include sys/types.h #include sys/stat.h #include vector #include unistd.h #include sys/time.h #include atomic #include boost/algorithm/string.hpp #include fstream namespace ns_util {class TimeUtil{public:static std::string GetTimeStamp(){// 获取时间戳 gettimeofdaystruct timeval _time;gettimeofday(_time, nullptr);return std::to_string(_time.tv_sec);}// 获得毫秒时间戳static std::string GetTimeMs(){struct timeval _time;gettimeofday(_time, nullptr);return std::to_string(_time.tv_sec * 1000 _time.tv_usec / 1000);}};const std::string temp_path ./temp/;class PathUtil{public:static std::string AddSuffix(const std::string file_name, const std::string suffix){std::string path_name temp_path;path_name file_name;path_name suffix;return path_name;}// 构建源文件路径后缀的完整文件名// 1234 - ./temp/1234.cppstatic std::string Src(const std::string file_name){return AddSuffix(file_name, .cpp);}// 构建可执行程序的完整路径 后缀名static std::string Exe(const std::string file_name){return AddSuffix(file_name, .exe);}static std::string CompilerError(const std::string file_name){return AddSuffix(file_name, .compile_stderr);}//-------------------------------------------------------------------// 构建该程序对应的标准错误完整的路径后缀名static std::string Stderr(const std::string file_name){return AddSuffix(file_name, .stderr);}static std::string Stdin(const std::string file_name){return AddSuffix(file_name, .stdin);}static std::string Stdout(const std::string file_name){return AddSuffix(file_name, .stdout);}};class FileUtil{public:static bool IsFileExists(const std::string path_name){struct stat st;if (stat(path_name.c_str(), st) 0){// 获取属性成功文件已经存在return true;}return false;}static std::string UniqFileName(){static std::atomic_uint id(0);id;// 毫秒时间戳std::string ms TimeUtil::GetTimeMs();std::string uniq_id std::to_string(id);return ms _ uniq_id;}static bool WriteFile(const std::string target, const std::string content){// waitingstd::ofstream out(target);if (!out.is_open()){return false;}out.write(content.c_str(), content.size());out.close();return true;}static bool ReadFile(const std::string target, std::string *content, bool keep false){(*content).clear();std::ifstream in(target);if (!in.is_open()){return false;}std::string line;// getline是按行保存的不保存行分隔符,自动去掉\n// 但是有些时候需要保留行分隔符// getline内部重载了强制类型转化while (std::getline(in, line)){(*content) line;(*content) (keep ? \n : );}in.close();return true;}};class StringUtil{public:/*str输入型目标要切分的字符串target输出型保存切分完毕的结果sep指定的分隔符*/static void SplitString(const std::string str,std::vectorstd::string *target,std::string sep){boost::split(*target,str,boost::is_any_of(sep),boost::algorithm::token_compress_on);//boost split}};} PathUtil路径工具 形成exe完整路径形成cpp完整路径形成compile_stderr完整路径形成stderr完整路径形成stdin完整路径完整路径指的是当前代码在本地上的保存路径即输入 1234 要形成 ./temp/1234.cpp等这里的相对路径形成依靠PathUtil工具TimeUtil时间工具 获取时间戳 get time of day获得好面时间戳用于形成文件唯一标识名字FileUtil文件工具 IsFileExits判断某文件是否存在UniqFileName形成文件唯一名字WriteFile向指定文件写入指定字符串ReadFile读取某文件的内容StringUtil字符串工具 使用boost库中的切分字符串Split() compiler编译服务设计 目的能够编译并运行代码得到格式化的相关结果 #pragma once#include iostream #includeunistd.h #includesys/types.h #includesys/stat.h #includefcntl.h #includesys/wait.h #include../comm/util.hpp #include../comm/log.hpp //只负责进行代码的编译 namespace ns_compiler{//引入路径拼接功能using namespace ns_util;using namespace ns_log;class Compiler{public:Compiler(){}~Compiler(){}//返回值是编译成功TRUEelse FALSE//输入参数是编译的文件名//file_name : 1234//1234 - ./temp/1234.cpp//1234 - ./temp/1234.exe//1234 - ./temp/1234.stderrstatic bool Compile(const std::string file_name){pid_t pid fork();if(pid 0){LOG(ERROR) 内部错误创建子进程失败\n;return false;}else if(pid 0)//子进程{umask(0);int _stderr open(PathUtil::CompilerError(file_name).c_str(),O_CREAT | O_WRONLY,0644);if(_stderr 0){LOG(WARNING)没有成功行成stderr文件\n;exit(1);}//重定向标准错误到_stderrdup2(_stderr,2);//程序替婚并不影响进程的文件描述符表//子进程调用编译器execlp(g,g,-o,PathUtil::Exe(file_name).c_str(),PathUtil::Src(file_name).c_str(),-stdc11,-D,COMPILER_ONLINE,nullptr);LOG(ERROR) 启动编译器g失败可能是参数错误\n;exit(2);}else//父进程{waitpid(pid,nullptr,0);//编译是否成功,就看有没有形成对应的可执行程序if(FileUtil::IsFileExists(PathUtil::Exe(file_name).c_str())){LOG(INFO) PathUtil::Src(file_name)编译成功!\n;return true;}}LOG(ERROR) 编译失败没有形成可执行程序,return false\n;return false;}}; }; compiler编译服务只管编译传过来的代码其他一律不管它只关心程序是否能够编译过 LOG日志的添加 #pragma once #includeiostream #includestring #includeutil.hpp namespace ns_log {using namespace ns_util;//日志等级enum{INFO,DEBUG,WARNING,ERROR ,FATAL};//LOG() messageinline std::ostream Log(const std::string level, const std::string file_name,int line){//添加日志等级std::string message [;messagelevel;message];//添加报错文件名称message[;messagefile_name;message];//添加报错行message[;messagestd::to_string(line);message];//添加日志时间戳message [;message TimeUtil::GetTimeStamp();message ];//cout 本质内部是包含缓冲区的std::coutmessage;//不要endl刷新return std::cout;}//LOG(INFo) message//开放式日志#define LOG(level) Log(#level,__FILE__,__LINE__) } runner运行功能设计 #pragma once#include iostream #includesys/time.h #includesys/resource.h #includesignal.h #include string #include unistd.h #include sys/types.h #include sys/stat.h #includesys/wait.h #includesys/time.h #includesys/resource.h #include fcntl.h #include ../comm/log.hpp #include ../comm/util.hpp namespace ns_runner {using namespace ns_log;using namespace ns_util;class Runner{public:Runner() {}~Runner() {}public://提供设置进程占用资源大小的接口static void SerProcLimit(int _cpu_limit,int _mem_limit){//设置CPU时长struct rlimit cpu_rlimit;cpu_rlimit.rlim_max RLIM_INFINITY;cpu_rlimit.rlim_cur _cpu_limit;setrlimit(RLIMIT_CPU,cpu_rlimit);//设置内存大小struct rlimit mem_rlimit;mem_rlimit.rlim_max RLIM_INFINITY;mem_rlimit.rlim_cur _mem_limit * 1024;//转化成kbsetrlimit(RLIMIT_AS,mem_rlimit);}// 指明文件名即可不需要带路径和后缀/*返回值如果是大于 0 程序异常了退出时收到了信号返回值就是对应的信号返回值 0 就是正常运行完毕结果是什么保存到了临时文件中我不清楚返回值 0 属于内部错误cpu_limit:该程序运行的时候可以使用的最大cpu的资源上限mem_limit该程序运行的时候可以使用的最大内存大小KB*/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 pid fork();if (pid 0){LOG(ERROR)运行时创建子进程失败\n;close(_stdin_fd);close(_stdout_fd);close(_stderr_fd);return -2; //代表创建子进程失败}else if (pid 0){dup2(_stdin_fd,0);dup2(_stdout_fd,1);dup2(_stderr_fd,2);SerProcLimit(cpu_limit,mem_limit);execl(_execute.c_str()/*我要执行谁*/,_execute.c_str()/*我想在命令航商如何执行*/,nullptr);exit(1);}else{int status 0;waitpid(pid,status,0);//程序运行异常一定是因为收到了信号LOG(INFO)运行完毕info(status 0x7F)\n;close(_stdin_fd);close(_stdout_fd);close(_stderr_fd);return status0x7F;}}}; } compile_run:编译并运行功能: #pragma once #include compiler.hpp #includeunistd.h #include runner.hpp #include ../comm/log.hpp #include ../comm/util.hpp #include jsoncpp/json/json.h #include signal.h namespace ns_compile_and_run {using namespace ns_log;using namespace ns_util;using namespace ns_compiler;using namespace ns_runner;class CompileAndRun{public:static void RemoveTempFile(const std::string file_name){//清理文件的个数是不确定的但是有哪些我们是知道的std::string _src PathUtil::Src(file_name);if(FileUtil::IsFileExists(_src))unlink(_src.c_str());std::string _compiler_error PathUtil::CompilerError(file_name);if(FileUtil::IsFileExists(_compiler_error))unlink(_compiler_error.c_str());std::string _execute PathUtil::Exe(file_name);if(FileUtil::IsFileExists(_execute)) unlink(_execute.c_str());std::string _stdin PathUtil::Stdin(file_name);if(FileUtil::IsFileExists(_stdin)) unlink(_stdin.c_str());std::string _stdout PathUtil::Stdout(file_name);if(FileUtil::IsFileExists(_stdout)) unlink(_stdout.c_str());std::string _stderr PathUtil::Stderr(file_name);if(FileUtil::IsFileExists(_stderr)) unlink(_stderr.c_str());}static std::string CodeToDesc(int code, std::string file_name) // code 0 0 0{std::string desc;switch (code){case 0:desc 编译运行成功;break;case -1:desc 用户提交的代码是空;break;case -2:desc 未知错误;break;case -3:// desc 编译发生报错;FileUtil::ReadFile(PathUtil::Stderr(file_name), desc, true);break;case -4:break;case SIGABRT:desc 内存超过范围;break;case SIGXCPU:desc CPU信号超时;break;case SIGFPE:desc 除零错误,浮点数溢出;break;default:desc 未知 std::to_string(code);break;}return desc;}/*输入code用户提交的代码input用户自己提交的代码对应的输入-》不做处理cpu_limit时间要求mem_limit:空间要求输出必填status状态码reason请求结果选填stdout我的的程序运行完的结果stderr我的程序运行完的错误结构参数in_json:{code:#include....input:,cpu_limit:1,mem_limit:10240}out_json:{status:0,reason:,stdout:,stderr:};*/static void Start(const std::string in_json, std::string *out_json){LOG(INFO)启动compile_and_run\n;Json::Value in_value;Json::Reader reader;reader.parse(in_json, in_value); // 最后再处理差错问题std::string code in_value[code].asString();std::string input in_value[input].asString();int cpu_limit in_value[cpu_limit].asInt();int men_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() 0){// 说明用户一行代码都没提交status_code -1;goto END;}// 形成的文件名只具有唯一性没有目录没有后缀// 毫秒计时间戳原子性递增的唯一值来保证唯一性file_name FileUtil::UniqFileName(); // 形成唯一文件名字LOG(DEBUG)调用UniqFileName()形成唯一名字file_name\n;run_result Runner::Run(file_name, cpu_limit, men_limit);if (!FileUtil::WriteFile(PathUtil::Src(file_name), code)) // 形成临时src文件.cpp{status_code -2; // 未知错误goto END;}if (!Compiler::Compile(file_name)){// 编译失败status_code -3;goto END;}run_result Runner::Run(file_name, cpu_limit, men_limit);if (run_result 0){// 服务器的内部错误包括不限于文件打开失败创建子进程失败等待status_code -2; // 未知错误goto END;}else if (run_result 0){status_code run_result;}else{// 运行成功status_code 0;}END:std::cout到达end语句std::endl;// status_codeout_value[status] status_code;out_value[reason] CodeToDesc(status_code, file_name);LOG(DEBUG)CodeToDesc(status_code, file_name);if (status_code 0){// 整个过程全部成功 , 这时候才需要运行结果std::string _stdout;FileUtil::ReadFile(PathUtil::Stdout(file_name), _stdout, true);out_value[stdout] _stdout; }else{std::string _stderr;FileUtil::ReadFile(PathUtil::CompilerError(file_name), _stderr, true);out_value[stderr] _stderr;}// 序列化Json::StyledWriter writer;*out_json writer.write(out_value);//清理所有的临时文件RemoveTempFile(file_name);}}; } compile_run:它的功能是接收远端传进来的json包并反序列化得到其中的代码与输入并调用compile进行编译 编译成功调用runner将代码运行起来-将执行结果分别保存到.exe、.stdin、.stdout 、.stderr、.compile_stderr文件中编译失败不调用runner 最后按对应构造json 返回给上级调用即write进out_json中,收尾清除创建的文件 compile_server .cc文件编写 #includecompile_run.hpp #includejsoncpp/json/json.h #include../comm/httplib.h using namespace ns_compile_and_run; using namespace httplib;//编译服务随时可能被多个人请求,必须保证传递上来的code形成源文件名称的时候要具有唯一性不然影响多个用户 void Usage(std::string proc) {std::cerr Usage:\n\tprocportstd::endl; } // ./compiler_server port int main(int argc,char *argv[]) {if(argc!2){Usage(argv[0]);}Server svr;svr.Get(/hello,[](const Request req,Response resp){resp.set_content(hello httplib,你好httplib,content_type: text/plain);});//svr.set_base_dir(./wwwroot);svr.Post(/compile_and_run,[](const Request req,Response resp){ //请求服务正文是我们想要的json串LOG(DEBUG)调用compile_and_run\n;std::string out_json;std::string in_json req.body;if(!in_json.empty()){LOG(DEBUG)当前的in_jsonin_json\n;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服务了// std::string code code;// Compiler::Compile(code);// Runner::Run(code);//0-----------------------测试代码-------------------//下面的工作充当客户端请求的json串// std::string in_json;// Json::Value in_value;// //R raw string 凡事在这个圆括号里面的东西就是字符串哪怕有一些特殊的字符串// in_value[code] R(#includeiostream// int main(){// std::cout测试成功std::endl;// int a 10;// a / 0;// return 0;// });// in_value[input] ;// in_value[cpu_limit] 1;// in_value[mem_limit] 10240 * 3;// Json::FastWriter writer;// std::coutin_jsonstd::endl;// in_json writer.write(in_value);// //这个是将来给客户端返回的字符串// std::string out_json;// CompileAndRun::Start(in_json,out_json);// std::coutout_jsonstd::endl;//0-----------------------------------------------------//提供的编译服务打包新城一个网络服务//这次直接用第三方库cpp-httplibreturn 0; } 直接引入的httplib库 设置好ip和端口就可以直接监听了 svr.get() 就是当对该服务器发起/hello 请求的时候我就会接受到该请求以Get的方式返回resp makefile 由于当前使用的c11的新特性引入了json库和多线程 compile_server:compile_server.ccg -o $ $^ -stdc11 -ljsoncpp -lpthread .PHONY:clean clean:rm -f compile_server 项目设计 -- 基于MVC结构的oj服务  本质建立一个小型网站 1.获取首页 2.编辑区域页面 3.提交判题功能编译并运行    MModel通常是和数据交互的模块比如对题库的增删改查文件版mysql版 Vview通常是拿到数据之后要进行构建网页渲染网页内容 Ccontrol控制器也就是我们核心业务逻辑 用户的请求服务路由功能 #include ../comm/httplib.h #include login.hpp #include iostream #include signal.h #includeoj_control.hpp using namespace httplib; using namespace ns_control; const std::string login_path ../oj_login/wwwroot/; static Control *ctrl_ptr nullptr; void Recovery(int signo) {ctrl_ptr-RecoveryMachine(); } int main() {signal(SIGQUIT,Recovery);// 用户请求的服务路由功能Server svr;Control ctrl;Login login;ctrl_ptr ctrl;/*1获取所有的题目列表*/svr.Get(R(/all_questions), [ctrl](const Request req, Response resp) {std::string html;ctrl.AllQuestions(html);resp.set_content(html, text/html;charsetutf-8);});// 2用户要根据题目编号来选择题目// 这里的\d是正则表达式 是匹配数字// R()保持原始字符串不会被特殊字符影响比如\d \r \n之类的不需要做相关的转义svr.Get(R(/question/(\d)), [ctrl](const Request req, Response resp) {std::string number req.matches[1];std::string html;ctrl.OneQuestion(number,html);resp.set_content(html,text/html;charsetutf-8);});// 3用户提交代码使用我们的判题功能1.没道题目的测试用例 2.compile_and_run)svr.Post(R(/judge/(\d)),[ctrl](const Request req, Response resp){std::string number req.matches[1];// resp.set_content(这是指定的一道题目的判题 number,// text/plain;charsetutf-8);std::string result_json;ctrl.Judge(number,req.body,result_json);resp.set_content(result_json,application/json;charsetutf-8);});svr.Post(R(/dealregister),[ctrl](const Request req, Response resp){int status 1;std::string in_json req.body;std::string out_json;if(!ctrl.UserRegister(in_json,out_json)){status 0;}LOG(INFO)用户注册status : status\n;Json::Value tmp;tmp[status] status;Json::FastWriter writer;std::string res writer.write(tmp);resp.set_content(res,application/json;charsetutf-8);});svr.Get(R(/my_login),[login,ctrl](const Request req,Response resp){//直接跳转到静态的htmlstd::string html;ctrl.Login(req.body,html);resp.set_content(html, text/html;charsetutf-8);});svr.Get(R(/register),[login,ctrl](const Request req,Response resp){//直接跳转到静态的htmlstd::string html;ctrl.Register(req.body,html);resp.set_content(html, text/html;charsetutf-8);});svr.set_base_dir(./wwwroot);svr.listen(0.0.0.0, 8080);return 0; } 这样当用户通过http请求我们的oj_server服务器的时候我们可以正确的路由到合适的功能 model功能提供对数据的操作文件版 #pragma once //文件版本 /* 编号 标题 难度 描述 时间内部空间内部处理两批文件构成 1.question.list题目列表不需要出现题目描述 2.需要题目的描述需要题目的预设置代码header.cpp,测试用例代码(tail.cpp)这两个内容是通过题目的编号产生关联的 */ #pragma once #include ../comm/log.hpp#include ../comm/util.hpp #include cassert #include fstream #include iostream #include stdlib.h #include string #include unordered_map #include vector // 根据题目list文件加载所有信息到内存中 // model:主要用来和数据进行交互对外提供访问数据的接口namespace ns_model { using namespace std; using namespace ns_log; using namespace ns_util; class Question { public:std::string number; // 题目编号std::string title; // 题目的标题std::string star; // 难度简单中等困难int cpu_limit; // 时间要求 sint mem_limit; // 空间要求 kbstd::string desc; // 题目的描述std::string header; // 题目预设给用户在线编辑器的代码std::string tail; // 题目的测试用例需要和header拼接形成完整代码 }; const std::string question_list ./questions/questions.list; const std::string question_path ./questions/; class Model { private:// 题号题目细节unordered_mapstring, Question questions;public:Model() { assert(LoadQuestionList(question_list)); }bool LoadQuestionList(const std::string question_list) {// 加载配置文件questions/question.list 题目编号文件ifstream in(question_list);if (!in.is_open()) {LOG(FATEL) 加载题库失败请检查是否存在题库文件 \n;return false;}std::string line;while (getline(in, line)) {vectorstring tokens;StringUtil::SplitString(line, tokens, );if (tokens.size() ! 5) {LOG(WARNING) 加载部分题目失败请检查文件格式 \n;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());std::string path question_path;path q.number;path /;FileUtil::ReadFile(path desc.txt, (q.desc), true);FileUtil::ReadFile(path header.cpp, (q.header), true);FileUtil::ReadFile(path tail.cpp, (q.tail), true);questions.insert({q.number, q});}LOG(INFO) 加载题库成功 \n;in.close();return true;}bool GetAllQuestion(vectorQuestion *out) {if (questions.size() 0) {LOG(ERROR) 用户获取题库失败 \n;return false;}for (const auto q : questions) {out-push_back(q.second); // fir是key sec是value}return true;}bool GetOneQuestion(const std::string number, Question *q) {const auto iter questions.find(number);if (iter questions.end()) {LOG(ERROR) 用户获取题目失败 number \n;return false;}(*q) iter-second;return true;}~Model() {} }; } // namespace ns_model 该设计中有一个 question的题目清单,像题库的目录一样填写每道题目的基本信息 对应的是: 1.题目编号 2.题目名字 3.题目难度 4.时间限制 5.空间限制 model功能提供对数据的操作数据库版 #pragma once //这个是mysql版本 /* 编号 标题 难度 描述 时间内部空间内部处理两批文件构成 1.question.list题目列表不需要出现题目描述 2.需要题目的描述需要题目的预设置代码header.cpp,测试用例代码(tail.cpp)这两个内容是通过题目的编号产生关联的 */ #pragma once #include ../comm/log.hpp#include ../comm/util.hpp #include cassert #include stdio.h #include fstream #include iostream #include stdlib.h #include string #include unordered_map #include vector#includeinclude/mysql.h // 根据题目list文件加载所有信息到内存中 // model:主要用来和数据进行交互对外提供访问数据的接口namespace ns_model { using namespace std; using namespace ns_log; using namespace ns_util; class Question { public:std::string number; // 题目编号std::string title; // 题目的标题std::string star; // 难度简单中等困难int cpu_limit; // 时间要求 sint mem_limit; // 空间要求 kbstd::string desc; // 题目的描述std::string header; // 题目预设给用户在线编辑器的代码std::string tail; // 题目的测试用例需要和header拼接形成完整代码 };const std::string oj_questions oj_questions; const std::string oj_user oj_user; const std::string host 127.0.0.1; const std::string user oj_client; const std::string passwd 123456; const std::string db oj; const int port 3306; class Model { private:// 题号题目细节unordered_mapstring, Question questions; public:Model() { }bool QueryMySql(const std::string sql,vectorQuestion *out){//创建mysql句柄MYSQL *my mysql_init(nullptr);//连接数据库if(mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0) nullptr){LOG(FATAL)连接数据库失败\n;return false;}//一定要设置该链接的编码格式默认是拉钉的mysql_set_character_set(my,utf8mb4);LOG(INFO)连接数据库成功\n;//执行sql语句if(0 ! mysql_query(my,sql.c_str())){LOG(WARNING) sql execute error!\n;return false;}MYSQL_RES *res mysql_store_result(my);//分析结果int rows mysql_num_rows(res); //获得行数量int cols mysql_num_fields(res);//获得列数量Question q;for(int i 0;irows;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);return true;}bool GetAllQuestion(vectorQuestion *out) {std::string sql select *from ;sql oj_questions;return QueryMySql(sql,out);}bool GetOneQuestion(const std::string number, Question *q) {bool res false;std::string sql select *from ;sqloj_questions;sql where number;sqlnumber;vectorQuestion result;if(QueryMySql(sql,result)){if(result.size() 1){*q result[0];res true;}}return res;}bool UserRegister(const std::string in_json,std::string* out_json){//这里先对in_json反序列化Json::Reader reader;Json::Value in_value;reader.parse(in_json,in_value);std::string number in_value[number].asString();std::string name in_value[name].asString();std::string password in_value[password].asString();int limit in_value[limit].asInt();int level in_value[level].asInt();//判断账号密码是否可行std::string sql select *from ;sqloj_user;sql where number;sqlnumber;//创建数据库MYSQL *my mysql_init(nullptr);//连接数据库if(mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0) nullptr){LOG(WARNING)连接到用户数据库失败\n;return false;}//一定要记得设置该链接的编码格式mysql_set_character_set(my,utf8);LOG(INFO)连接懂啊用户数据库成功\n;if(0 ! mysql_query(my,sql.c_str())){LOG(WARNING) sql execute error!\n;return false;}MYSQL_RES *res mysql_store_result(my);if(mysql_num_rows(res) 0)//获得行数量{ //当前输入的数据可以创建用户MYSQL_STMT *stmt mysql_stmt_init(my);const char* query insert into oj_user values (?,?,?,?,?);if(mysql_stmt_prepare(stmt,query,strlen(query)) ! 0){LOG(WARNING)stmt出现错误\n;mysql_stmt_close(stmt);mysql_close(my);return false;}//下面开始绑定MYSQL_BIND bind_params[5];memset(bind_params,0,sizeof bind_params);bind_params[0].buffer_type MYSQL_TYPE_STRING;bind_params[0].buffer (char*)number.c_str();bind_params[0].buffer_length number.size();bind_params[1].buffer_type MYSQL_TYPE_STRING;bind_params[1].buffer (char*)name.c_str();bind_params[1].buffer_length name.size();bind_params[2].buffer_type MYSQL_TYPE_STRING;bind_params[2].buffer (char*)password.c_str();bind_params[2].buffer_length password.size();bind_params[3].buffer_type MYSQL_TYPE_LONG;bind_params[3].buffer limit;bind_params[3].is_unsigned 1;bind_params[4].buffer_type MYSQL_TYPE_LONG;bind_params[4].buffer level;bind_params[4].is_unsigned 1;if(mysql_stmt_bind_param(stmt,bind_params) !0){LOG(WARNING) 绑定stmt参数出错\n;mysql_stmt_close(stmt);mysql_close(my);return false;}//执行插入语句if(mysql_stmt_execute(stmt)!0){LOG(WARNING)执行stmt语句的时候出现错误...\n;mysql_stmt_close(stmt);mysql_close(my);return false;}mysql_stmt_close(stmt);mysql_close(my);return true;}else{//服务器有重复的用户num 不允许再创建了return false;}//保存到服务器//这里out_json暂时没有用没有要返回的值return true;}~Model() {} }; } // namespace ns_model control逻辑控制模块 #pragma once#includeiostream #includestring #includecassert #includealgorithm #includefstream #includejsoncpp/json/json.h #includevector #includemutex #includeoj_view.hpp // #includeoj_model.hpp #includeoj_model2.hpp #include../comm/log.hpp #include../comm/util.hpp #include../comm/httplib.h namespace ns_control {using namespace std;using namespace httplib;using namespace ns_log;using namespace ns_util;using namespace ns_model; using namespace ns_view;//提供服务的主机的内容class Machine{public:std::string ip; //编译服务器的ipint port; //编译服务器的端口uint64_t load; //编译服务器负载std::mutex *mtx;//mutex是禁止拷贝的,使用指针来完成public:Machine():ip(),port(0),load(0),mtx(nullptr){}~Machine(){}public:void ResetLoad(){if(mtx)mtx-lock();load 0;LOG(DEBUG)当前ip:ip端口port的load已经清除load load\n;if(mtx)mtx-unlock();}//提升主机负载void IncLoad(){if(mtx) mtx-lock();load;if(mtx) mtx-unlock();}//减少主机负载void DecLoad(){if(mtx) mtx-lock();--load;if(mtx) mtx-unlock();}//获取主机负载,没有太大的意义只是为了同一接口uint64_t Load(){uint64_t _load 0;if(mtx) mtx-lock();_load load;if(mtx) mtx-unlock();return _load;}};const std::string service_machine ./conf/service_machine.conf;//负载均衡模块class LoadBalance{ private://可以给我们提供编译服务的所有的主机//每一台主机都有自己的下标充当当前主机的idstd::vectorMachine machines; //所有在线的主机std::vectorint online;//所有离线主机的idstd::vectorint offline;//保证选择主机上的这个东西要保证数据安全std::mutex mtx;public:LoadBalance(){assert(LoadConf(service_machine));LOG(INFO)加载service_machine 成功\n;}~LoadBalance(){}public:bool LoadConf(const std::string machine_cof){std::ifstream in(machine_cof);if(!in.is_open())\{LOG(FATAL) 加载machine_cof失败\n;return false;}std::string line;while (getline(in,line)){std::vectorstd::string tokens;StringUtil::SplitString(line,tokens,:);if(tokens.size()!2){LOG(WARNING) 切分line失败\n;std::couttokens[0]:tokens[1]std::endl;continue;}//LOG(INFO) 切分tokens[0]tokens[1]成功\n;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.随机数法 hash//2.轮询 hashint online_num online.size();//在线主机的个数if(online_num 0){mtx.unlock();LOG(FATAL) 所有的后端编译主机已经全部离线请后端的尽快重启\n;return false;}LOG(DEBUG)online:online.size()\n;//通过编译找到负载最小的机器*id online[0];*m machines[online[0]];uint64_t min_load machines[online[0]].Load();for(int i 1;ionline_num;i){uint64_t curr_load machines[online[i]].Load();if(min_load curr_load){min_load curr_load;*id online[i];*m machines[online[i]];}}mtx.unlock();return true;}void OfflineMachine(int which){mtx.lock();for(auto iter online.begin();iter!online.end();iter){if(*iter which){//要离线的主机找到了machines[which].ResetLoad();LOG(DEBUG)当前离线主机的负载更改为machines[which].load;online.erase(iter);offline.push_back(which);break;//因为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;LOG(INFO)online:online.size()offlineoffline.size()\n;}void ShowMachines(){mtx.lock();LOG(INFO)online:online.size()offlineoffline.size()\n;mtx.unlock();}};//这是我们核心业务逻辑的控制器class Control{private:Model model_;//提供后台数据View view_; //提供网页渲染功能LoadBalance load_blance_; //核心负载均衡器public:void RecoveryMachine(){load_blance_.OnlineMachine();}//根据题目数据构建网页//html输出型参数bool AllQuestions(string *html){bool ret true;vectorQuestion all;if(model_.GetAllQuestion(all)){sort(all.begin(),all.end(),[](const Question q1,const Question q2){return atoi(q1.number.c_str()) atoi(q2.number.c_str());});//获取题目信息 成功将所有的题目数据构建成网页view_.AllExpandHtml(all,html);}else{*html获取题目失败形成题目列表失败;ret false;}return ret;}bool OneQuestion(const string number,string *html){Question q;bool ret true;if(model_.GetOneQuestion(number,q)){//获取指定信息的题目成功构建程网页view_.OneExpandHtml(q,html);}else{*html获取指定题目题目失败形成题目列表失败;ret false;}return ret;}void Login(const std::string in_json,std::string *out_json){//in_json是发送过来的请求数据用户的账号等待//返回渲染的登录界面view_.LoginExpandHtml(out_json);}void Register(const std::string in_json,std::string *out_json){if(view_.RegisterExpandHtml(out_json)){LOG(INFO)插入成功\n;}else{LOG(INFO)插入失败可能是重复的用户\n;}}bool UserRegister(const std::string in_json,std::string *out_json){return model_.UserRegister(in_json,out_json);}//id:: 100 //code:include//input:void Judge(const std::string number,const std::string in_json,std::string *out_json){// LOG(INFO)调用Judge功能\n;// LOG(DEBUG)in_json\nnumber:number\n;//0.根据题目编号拿到题目细节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 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.选择负载最低的主机然后发起HTTP请求得到结果//规则一直选择直到主机可用否则就是全部挂掉while(true){int id 0;Machine *m nullptr;if(!load_blance_.SmartChoice(id,m)){ break;}//4.*out_json 将结果复制给out_jsonClient cli(m-ip,m-port);m-IncLoad();LOG(DEBUG)选择主机成功主机id:id\n详情m-ip:m-port当前主机负载m-Load()\n;if(auto res cli.Post(/compile_and_run,compile_string,application/json;charsetutf-8)){//将我们的结果返回给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_.ShowMachines();//仅仅为了调试}//m-DecLoad();}}Control(){}~Control(){}}; } control模块实现了 负载均衡 负载均衡 第一种随机数hash第二种轮询hash 本文是在用轮询hash为了实现负载均衡所有要把所有主机管理起来有了Machine类 std::string ip 编译服务器的ipint port编译服务器的端口uint64_t load 编译服务器的负载std::mutex *mtx每个机器可能会同时被多个用户访问所以要有锁来保证临界资源并且mutex是不允许拷贝的所以这里直接用指针这样在赋值构造和拷贝构造就没事了 view渲染功能将后端的代码渲染到html返回给前端 这里就要使用到ctemplate库了 #pragma once#includeiostream #includestring #includectemplate/template.h // #includeoj_model.hpp #includeoj_model2.hppnamespace ns_view {using namespace ns_model;const std::string template_path ./template_html/;const std::string login_path ./login_html/;class View{public:View(){}~View(){}bool RegisterExpandHtml(std::string *html){//新城路径std::string src_html login_path register.html;//形成数据字典ctemplate::TemplateDictionary root(register);//获取渲染的网页ctemplate::Template *tpl ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);//开始渲染tpl-Expand(html,root);return true;}void LoginExpandHtml(std::string *html){//形成路径std::string src_html login_path login.html;//形成数据字典ctemplate::TemplateDictionary root(my_login);//获取渲染网页ctemplate::Template *tpl ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);//开始渲染tpl-Expand(html,root);}void AllExpandHtml(const vectorQuestion questions,std::string *html){// 题目的编号 题目的标题 题目的难度// 推荐使用表格显示//1。形成路径std::string src_html template_path all_questions.html;LOG(INFO)形成路径成功: src_html \n;//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);LOG(INFO)获取渲染网页的html成功\n;//4.开始完成渲染功能tpl-Expand(html,root);LOG(INFO)渲染成功\n;}void OneExpandHtml(const Question q,std::string *html){//形成路径std::string src_html template_path one_question.html;LOG(DEBUG)one expand html :src_html\n;//q.desc//形成数字典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);//获取被渲染的htmlctemplate::Template *tpl ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);//开始渲染功能tpl-Expand(html,root);}}; } 总结  1.前端的代码在博客最上端绑定的文件当中 篇幅太长不展示出来了 2.该项目的技术栈众多是c后端和前端进行交互的一个项目 3.项目的难点有负载均衡的分配到每一台编译服务器、容错处理能够处理多种不同的错误原因、并发处理要对临界资源的管理、以及高并发访问的话要对效率有所保证毕竟在线oj服务是具有时效性的 4.debug困难要在test.cc下测试成功后再进行编写便于修改bug
http://www.pierceye.com/news/96529/

相关文章:

  • 廊坊网站公司dw做网站背景音乐
  • 阜南做网站搜索引擎优化seo多少钱
  • 贵州建设厅网站怎样查询电工证天津网站备案
  • 常州做网站的公司在盐城做网站的网络公司电话
  • seo站外推广如何用wampp 做网站
  • 怎样用手机做网站中企动力百度百科
  • 哪些网站可以做任务挣钱免费app软件
  • 国内简约网站平潭县机场建设网站
  • wordpress 全站通知wordpress怎样打开速度快
  • 广州市建设职业培训学校网站移除wordpress版本
  • 如何申请一个网站 新网动画制作大师
  • 动易后台 网站统计调查 报表类型怎样使用手机相册备份网站源码
  • 做网站放到百度上需要什么查看wordpress使用什么主题
  • 深圳企业网站seo郑州汉狮专业做网站公司
  • 广东网站建设多少钱辛集专业网站建设
  • 怎样做网站公司的销售小程序推广计划怎么赚钱
  • 网站开发文档编写wordpress小说站群
  • 南宁网站开发推广网站html模板下载
  • 网络编辑的网站建设题二手域名交易平台
  • 定制网站开发商业计划书贵南县网站建设公司
  • 如何免费发布个人网站网站项目需求分析
  • 太原免费网站建设网站开发合作协议书
  • 深圳龙华做网站上海响应式网站制作公司
  • 招投标 网站建设专业型网站和个人网站
  • 网站建设需要那些基础增城线上教学
  • 专注移动网站建设免费咨询电脑维修
  • 六里桥做网站公司惠州做网站 百度优化
  • 做网站怎么选择上市公司wordpress 进销存
  • 做视频网站用哪个模板昆明贤邦网站建设
  • 自建网站工具wordpress仿内涵段子