国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

基于負(fù)載均衡的在線OJ實(shí)戰(zhàn)項(xiàng)目

這篇具有很好參考價(jià)值的文章主要介紹了基于負(fù)載均衡的在線OJ實(shí)戰(zhàn)項(xiàng)目。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

前言:

該篇講述了實(shí)現(xiàn)基于負(fù)載均衡式的在線oj,即類似在線編程做題網(wǎng)站一樣,文章盡可能詳細(xì)講述細(xì)節(jié)即實(shí)現(xiàn),便于大家了解學(xué)習(xí)。

文章將采用單篇不分段形式(ps:切著麻煩),附圖文,附代碼,代碼部署在云服務(wù)器上

技術(shù)棧

  • C++ STL標(biāo)準(zhǔn)庫

  • Boost 標(biāo)準(zhǔn)庫

  • cpp-httpib 開源庫

  • ctemplate 第三方開源前端網(wǎng)頁渲染庫

  • jsoncpp 第三方開源序列化、反序列化庫

  • 負(fù)載均衡的設(shè)計(jì)

  • 多進(jìn)程、多線程

  • MYSQL C connect

  • Ace前端在線編輯器

  • html/cdd/js/jquery/ajax

開發(fā)環(huán)境

  • vscode
  • mysql workbench
  • Centos 7云服務(wù)器

宏觀結(jié)構(gòu)

  • comm:公共模塊
  • compile_sever:編譯運(yùn)行模塊
  • oj_server:獲取題目,負(fù)載均衡等

基于負(fù)載均衡的在線OJ實(shí)戰(zhàn)項(xiàng)目,實(shí)戰(zhàn)項(xiàng)目,負(fù)載均衡,運(yùn)維,linux,c++,開發(fā)語言,javascript,ajax

?文章來源地址http://www.zghlxwxcb.cn/news/detail-682945.html

?


項(xiàng)目演示:?


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


?



項(xiàng)目設(shè)計(jì) -- 編譯服務(wù)

工具類的準(zhǔn)備:

供程序中各個(gè)部分調(diào)用的方法類:

#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()
        {
            // 獲取時(shí)間戳 gettimeofday
            struct timeval _time;
            gettimeofday(&_time, nullptr);
            return std::to_string(_time.tv_sec);
        }
        // 獲得毫秒時(shí)間戳
        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;
        }
        // 構(gòu)建源文件路徑+后綴的完整文件名
        // 1234 -> ./temp/1234.cpp
        static std::string Src(const std::string &file_name)
        {
            return AddSuffix(file_name, ".cpp");
        }
        // 構(gòu)建可執(zhí)行程序的完整路徑 + 后綴名
        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");
        }
        //-------------------------------------------------------------------
        // 構(gòu)建該程序?qū)?yīng)的標(biāo)準(zhǔn)錯(cuò)誤完整的路徑+后綴名
        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)
            {
                // 獲取屬性成功,文件已經(jīng)存在
                return true;
            }
            return false;
        }
        static std::string UniqFileName()
        {
            static std::atomic_uint id(0);
            id++;
            // 毫秒時(shí)間戳
            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)
        {
            // waiting
            std::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是按行保存的,不保存行分隔符,自動(dòng)去掉\n
            // 但是有些時(shí)候,需要保留行分隔符
            // getline內(nèi)部重載了強(qiáng)制類型轉(zhuǎn)化
            while (std::getline(in, line))
            {
                (*content) += line;
                (*content) += (keep ? "\n" : "");
            }
            in.close();
            return true;
        }
    };

    class StringUtil
    {
        public:
        /*
        str:輸入型,目標(biāo)要切分的字符串
        target:輸出型,保存切分完畢的結(jié)果
        sep:指定的分隔符
        */
        static void SplitString(const std::string &str,std::vector<std::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完整路徑
    • 完整路徑指的是當(dāng)前代碼在本地上的保存路徑:即輸入 1234 要形成 ./temp/1234.cpp等這里的相對路徑形成依靠PathUtil工具
  • TimeUtil:時(shí)間工具
    • 獲取時(shí)間戳 get time of day
    • 獲得好面時(shí)間戳,用于形成文件唯一標(biāo)識(shí)(名字)
  • FileUtil:文件工具
    • IsFileExits:判斷某文件是否存在
    • UniqFileName:形成文件唯一名字
    • WriteFile:向指定文件寫入指定字符串
    • ReadFile:讀取某文件的內(nèi)容
  • StringUtil:字符串工具
    • 使用boost庫中的切分字符串Split()

?

compiler編譯服務(wù)設(shè)計(jì) :

目的:能夠編譯并運(yùn)行代碼,得到格式化的相關(guān)結(jié)果

基于負(fù)載均衡的在線OJ實(shí)戰(zhàn)項(xiàng)目,實(shí)戰(zhàn)項(xiàng)目,負(fù)載均衡,運(yùn)維,linux,c++,開發(fā)語言,javascript,ajax

?

#pragma once

#include <iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/wait.h>
#include"../comm/util.hpp"
#include"../comm/log.hpp"
//只負(fù)責(zé)進(jìn)行代碼的編譯
namespace ns_compiler{
    //引入路徑拼接功能
    using namespace ns_util;
    using namespace ns_log;
    class Compiler{
        public:
        Compiler()
        {}
        ~Compiler()
        {}
        //返回值是編譯成功TRUE;else FALSE
        //輸入?yún)?shù)是編譯的文件名
        //file_name : 1234
        //1234 -> ./temp/1234.cpp
        //1234 -> ./temp/1234.exe
        //1234 -> ./temp/1234.stderr
        static bool Compile(const std::string &file_name)
        {
            pid_t pid = fork();
            if(pid < 0)
            {
                LOG(ERROR) << "內(nèi)部錯(cuò)誤,創(chuàng)建子進(jìn)程失敗"<<"\n";
                return false;
            }
            else if(pid == 0)//子進(jìn)程
            {
                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);
                }
                //重定向標(biāo)準(zhǔn)錯(cuò)誤到_stderr
                dup2(_stderr,2);
                //程序替婚,并不影響進(jìn)程的文件描述符表
                //子進(jìn)程:調(diào)用編譯器
                execlp("g++","g++","-o",PathUtil::Exe(file_name).c_str(),PathUtil::Src(file_name).c_str(),"-std=c++11","-D","COMPILER_ONLINE",nullptr);
                LOG(ERROR) <<"啟動(dòng)編譯器g++失敗,可能是參數(shù)錯(cuò)誤"<<"\n";
                exit(2);
            }
            else//父進(jìn)程
            {
                waitpid(pid,nullptr,0);
                //編譯是否成功,就看有沒有形成對應(yīng)的可執(zhí)行程序
                if(FileUtil::IsFileExists(PathUtil::Exe(file_name).c_str())){
                    LOG(INFO) <<PathUtil::Src(file_name)<<"編譯成功!"<<"\n";
                    return true;
                }
            }
            LOG(ERROR) <<"編譯失敗,沒有形成可執(zhí)行程序,return false"<<"\n";
            return false;

        }
    };
};

compiler編譯服務(wù)只管編譯傳過來的代碼,其他一律不管,它只關(guān)心程序是否能夠編譯過

LOG日志的添加:

#pragma once
#include<iostream>
#include<string>
#include"util.hpp"
namespace ns_log
{
    using namespace ns_util;
    //日志等級
    enum{
        INFO,
        DEBUG,
        WARNING,
        ERROR ,
        FATAL
    };
    //LOG() << "message"
    inline std::ostream &Log(const std::string &level, const std::string &file_name,int line)
    {
        //添加日志等級
        std::string message = "[";
        message+=level;
        message+="]";

        //添加報(bào)錯(cuò)文件名稱
        message+="[";
        message+=file_name;
        message+="]";

        //添加報(bào)錯(cuò)行
        message+="[";
        message+=std::to_string(line);
        message+="]";

        //添加日志時(shí)間戳

        message += "[";
        message += TimeUtil::GetTimeStamp();
        message += "]";

        //cout 本質(zhì)內(nèi)部是包含緩沖區(qū)的
        std::cout<<message;//不要endl刷新
        return std::cout;
    }
    //LOG(INFo) << "message"
    //開放式日志
    #define LOG(level) Log(#level,__FILE__,__LINE__)
}

runner運(yùn)行功能設(shè)計(jì):

#pragma once

#include <iostream>
#include<sys/time.h>
#include<sys/resource.h>
#include<signal.h>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include<sys/wait.h>
#include<sys/time.h>
#include<sys/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:
        //提供設(shè)置進(jìn)程占用資源大小的接口
        static void SerProcLimit(int _cpu_limit,int _mem_limit)
        {
            //設(shè)置CPU時(shí)長
            struct rlimit cpu_rlimit;
            cpu_rlimit.rlim_max = RLIM_INFINITY;
            cpu_rlimit.rlim_cur = _cpu_limit;
            setrlimit(RLIMIT_CPU,&cpu_rlimit);
            //設(shè)置內(nèi)存大小
            struct rlimit mem_rlimit;
            mem_rlimit.rlim_max = RLIM_INFINITY;
            mem_rlimit.rlim_cur = _mem_limit * 1024;//轉(zhuǎn)化成kb
            setrlimit(RLIMIT_AS,&mem_rlimit);
        }
        // 指明文件名即可,不需要帶路徑和后綴
        /*
        返回值如果是大于 0 :程序異常了,退出時(shí)收到了信號,返回值就是對應(yīng)的信號
        返回值 == 0 就是正常運(yùn)行完畢,結(jié)果是什么保存到了臨時(shí)文件中,我不清楚
        返回值 < 0 屬于內(nèi)部錯(cuò)誤
        cpu_limit:該程序運(yùn)行的時(shí)候,可以使用的最大cpu的資源上限
        mem_limit:該程序運(yùn)行的時(shí)候,可以使用的最大內(nèi)存大小KB
        */
        static int Run(const std::string &file_name,int cpu_limit,int mem_limit)
        {
            /*程序運(yùn)行:
            1.代碼跑完結(jié)果爭取
            2.代碼跑完結(jié)果不正確
            3.代碼沒跑完,異常了
            run不需要考慮運(yùn)行完后正確與否,只管跑

            首先我們必須知道可執(zhí)行程序是誰?
            標(biāo)準(zhǔn)輸入:不處理
            標(biāo)準(zhǔn)輸入:程序運(yùn)行完成,輸出結(jié)果是什么
            標(biāo)準(zhǔn)錯(cuò)誤:運(yùn)行時(shí)錯(cuò)誤信息
            */
            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)<<"運(yùn)行時(shí)打開標(biāo)準(zhǔn)文件失敗"<<"\n";
                return -1; // 代表打開文件失敗
            }
            pid_t pid = fork();
            if (pid < 0)
            {
                LOG(ERROR)<<"運(yùn)行時(shí)創(chuàng)建子進(jìn)程失敗"<<"\n";
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2; //代表創(chuàng)建子進(jìn)程失敗
            }
            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()/*我要執(zhí)行誰*/,_execute.c_str()/*我想在命令航商如何執(zhí)行*/,nullptr);
                exit(1);
            }
            else
            {
                
                int status = 0;
                waitpid(pid,&status,0);
                //程序運(yùn)行異常,一定是因?yàn)槭盏搅诵盘?                LOG(INFO)<<"運(yùn)行完畢,info:"<<(status & 0x7F)<<"\n";
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return status&0x7F;
            }
        }
    };
}

compile_run:編譯并運(yùn)行功能:

#pragma once
#include "compiler.hpp"
#include<unistd.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)
        {
            //清理文件的個(gè)數(shù)是不確定的,但是有哪些我們是知道的
            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 = "編譯運(yùn)行成功";
                break;
            case -1:
                desc = "用戶提交的代碼是空";
                break;
            case -2:
                desc = "未知錯(cuò)誤";
                break;
            case -3:
                // desc = "編譯發(fā)生報(bào)錯(cuò)";
                FileUtil::ReadFile(PathUtil::Stderr(file_name), &desc, true);
                break;
            case -4:
                break;
            case SIGABRT:
                desc = "內(nèi)存超過范圍";
                break;
            case SIGXCPU:
                desc = "CPU信號超時(shí)";
                break;
            case SIGFPE:
                desc = "除零錯(cuò)誤,浮點(diǎn)數(shù)溢出";
                break;
            default:
                desc = "未知:" + std::to_string(code);
                break;
            }
            return desc;
        }
        /*
        輸入:
        code:用戶提交的代碼
        input:用戶自己提交的代碼,對應(yīng)的輸入-》不做處理
        cpu_limit:時(shí)間要求
        mem_limit:空間要求
        輸出:
        必填:
        status:狀態(tài)碼
        reason:請求結(jié)果
        選填:
        stdout:我的的程序運(yùn)行完的結(jié)果
        stderr:我的程序運(yùn)行完的錯(cuò)誤結(jié)構(gòu)

        參數(shù):
        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)<<"啟動(dòng)compile_and_run"<<"\n";
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json, in_value); // 最后再處理差錯(cuò)問題

            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; // 需要內(nèi)部形成的唯一文件名

            if (code.size() == 0)
            {
                // 說明用戶一行代碼都沒提交
                status_code = -1;
                goto END;
            }

            // 形成的文件名只具有唯一性,沒有目錄沒有后綴
            // 毫秒計(jì)時(shí)間戳+原子性遞增的唯一值:來保證唯一性
            file_name = FileUtil::UniqFileName(); // 形成唯一文件名字
            LOG(DEBUG)<<"調(diào)用UniqFileName()形成唯一名字"<<file_name<<"\n";

            run_result = Runner::Run(file_name, cpu_limit, men_limit);
            if (!FileUtil::WriteFile(PathUtil::Src(file_name), code)) // 形成臨時(shí)src文件.cpp
            {
                status_code = -2; // 未知錯(cuò)誤
                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)
            {
                // 服務(wù)器的內(nèi)部錯(cuò)誤,包括不限于文件打開失敗,創(chuàng)建子進(jìn)程失敗等待
                status_code = -2; // 未知錯(cuò)誤
                goto END;
            }
            else if (run_result > 0)
            {
                status_code = run_result;
            }
            else
            {
                // 運(yùn)行成功
                status_code = 0;
            }
        END:
            std::cout<<"到達(dá)end語句"<<std::endl;
            // status_code
            out_value["status"] = status_code;
            out_value["reason"] = CodeToDesc(status_code, file_name);
            LOG(DEBUG)<<CodeToDesc(status_code, file_name);
            if (status_code == 0)
            {
                // 整個(gè)過程全部成功 , 這時(shí)候才需要運(yùn)行結(jié)果
                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);

            //清理所有的臨時(shí)文件
            RemoveTempFile(file_name);
        }
    };
}

compile_run:它的功能是接收遠(yuǎn)端傳進(jìn)來的json包,并反序列化得到其中的代碼與輸入,并調(diào)用compile進(jìn)行編譯

  • 編譯成功:調(diào)用runner將代碼運(yùn)行起來->將執(zhí)行結(jié)果分別保存到.exe、.stdin、.stdout 、.stderr、.compile_stderr文件中
  • 編譯失敗:不調(diào)用runner

最后按對應(yīng)構(gòu)造json 返回給上級調(diào)用,即write進(jìn)out_json中,收尾清除創(chuàng)建的文件

?

compile_server .cc文件編寫:

?

#include"compile_run.hpp"
#include<jsoncpp/json/json.h>
#include"../comm/httplib.h"
using namespace ns_compile_and_run;
using namespace httplib;

//編譯服務(wù)隨時(shí)可能被多個(gè)人請求,必須保證傳遞上來的code,形成源文件名稱的時(shí)候,要具有唯一性,不然影響多個(gè)用戶
void Usage(std::string proc)
{
    std::cerr <<"Usage:"<<"\n\t"<<proc<<"port"<<std::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){  
        //請求服務(wù)正文是我們想要的json串
        LOG(DEBUG)<<"調(diào)用compile_and_run"<<"\n";
        std::string out_json;
        std::string in_json = req.body;
        if(!in_json.empty()){
            LOG(DEBUG)<<"當(dāng)前的in_json"<<in_json<<"\n";
            CompileAndRun::Start(in_json,&out_json);
            resp.set_content(out_json,"application/json;charset=utf-8");
        }
    });
    svr.listen("0.0.0.0",atoi(argv[1]));//啟動(dòng)http服務(wù)了

    // std::string code = "code";
    // Compiler::Compile(code);
    // Runner::Run(code);


    //0-----------------------測試代碼-------------------
    //下面的工作,充當(dāng)客戶端請求的json串
    // std::string in_json;
    // Json::Value in_value;
    // //R"()" raw string 凡事在這個(gè)圓括號里面的東西,就是字符串哪怕有一些特殊的字符串
    // in_value["code"] =R"(#include<iostream>
    // 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::cout<<in_json<<std::endl;
    // in_json = writer.write(in_value);

    // //這個(gè)是將來給客戶端返回的字符串
    // std::string out_json;
    // CompileAndRun::Start(in_json,&out_json);

    // std::cout<<out_json<<std::endl;
    //0-----------------------------------------------------


    //提供的編譯服務(wù),打包新城一個(gè)網(wǎng)絡(luò)服務(wù)
    //這次直接用第三方庫,cpp-httplib

    return 0;
}

直接引入的httplib庫,?設(shè)置好ip和端口就可以直接監(jiān)聽了

  • svr.get() :就是當(dāng)對該服務(wù)器發(fā)起/hello 請求的時(shí)候,我就會(huì)接受到該請求,以Get的方式返回resp

makefile:

由于當(dāng)前使用的c++11的新特性,引入了json庫,和多線程

compile_server:compile_server.cc
	g++ -o $@ $^ -std=c++11 -ljsoncpp -lpthread
.PHONY:clean
clean:
	rm -f compile_server

項(xiàng)目設(shè)計(jì) -- 基于MVC結(jié)構(gòu)的oj服務(wù)?

本質(zhì):建立一個(gè)小型網(wǎng)站

1.獲取首頁

2.編輯區(qū)域頁面

3.提交判題功能(編譯并運(yùn)行)?

?

M:Model,通常是和數(shù)據(jù)交互的模塊,比如對題庫的增刪改查(文件版,mysql版)

V:view,通常是拿到數(shù)據(jù)之后,要進(jìn)行構(gòu)建網(wǎng)頁,渲染網(wǎng)頁內(nèi)容

C:control,控制器,也就是我們核心業(yè)務(wù)邏輯

用戶的請求服務(wù)路由功能:


#include "../comm/httplib.h"
#include "login.hpp"
#include <iostream>
#include <signal.h>
#include"oj_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);
  // 用戶請求的服務(wù)路由功能
  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;charset=utf-8");
  });

  //   2用戶要根據(jù)題目編號來選擇題目
  // 這里的\d是正則表達(dá)式 + 是匹配數(shù)字
  // R"()"保持原始字符串不會(huì)被特殊字符影響比如\d \r \n之類的不需要做相關(guān)的轉(zhuǎ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;charset=utf-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;charset=utf-8");
    std::string result_json;
    ctrl.Judge(number,req.body,&result_json);
    resp.set_content(result_json,"application/json;charset=utf-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;charset=utf-8");
  });

  svr.Get(R"(/my_login)",[&login,&ctrl](const Request &req,Response &resp){
    //直接跳轉(zhuǎn)到靜態(tài)的html
    std::string html;
    ctrl.Login(req.body,&html);
    resp.set_content(html, "text/html;charset=utf-8");
  });

  svr.Get(R"(/register)",[&login,&ctrl](const Request &req,Response &resp){
    //直接跳轉(zhuǎn)到靜態(tài)的html
    std::string html;
    ctrl.Register(req.body,&html);
    resp.set_content(html, "text/html;charset=utf-8");
  });

  svr.set_base_dir("./wwwroot");
  svr.listen("0.0.0.0", 8080);
  return 0;
}

這樣當(dāng)用戶通過http請求我們的oj_server服務(wù)器的時(shí)候我們可以正確的路由到合適的功能

model功能:提供對數(shù)據(jù)的操作(文件版)

#pragma once
//文件版本
/*
編號
標(biāo)題
難度
描述
時(shí)間(內(nèi)部),空間(內(nèi)部處理)

兩批文件構(gòu)成
1.question.list:題目列表:不需要出現(xiàn)題目描述
2.需要題目的描述,需要題目的預(yù)設(shè)置代碼(header.cpp),測試用例代碼(tail.cpp)

這兩個(gè)內(nèi)容是通過題目的編號,產(chǎn)生關(guān)聯(lián)的
*/
#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>
// 根據(jù)題目list文件,加載所有信息到內(nèi)存中
// model:主要用來和數(shù)據(jù)進(jìn)行交互,對外提供訪問數(shù)據(jù)的接口

namespace ns_model {
using namespace std;
using namespace ns_log;
using namespace ns_util;
class Question {
public:
  std::string number; // 題目編號
  std::string title;  // 題目的標(biāo)題
  std::string star;   // 難度:簡單中等困難
  int cpu_limit;      // 時(shí)間要求 s
  int mem_limit;      // 空間要求 kb
  std::string desc;   // 題目的描述
  std::string header; // 題目預(yù)設(shè)給用戶在線編輯器的代碼
  std::string tail; // 題目的測試用例,需要和header拼接形成完整代碼
};
const std::string question_list = "./questions/questions.list";
const std::string question_path = "./questions/";
class Model {
private:
  // 題號:題目細(xì)節(jié)
  unordered_map<string, 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)) {
      vector<string> 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(vector<Question> *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

該設(shè)計(jì)中有一個(gè) question的題目清單,像題庫的目錄一樣,填寫每道題目的基本信息:

對應(yīng)的是:

1.題目編號 2.題目名字 3.題目難度 4.時(shí)間限制 5.空間限制

基于負(fù)載均衡的在線OJ實(shí)戰(zhàn)項(xiàng)目,實(shí)戰(zhàn)項(xiàng)目,負(fù)載均衡,運(yùn)維,linux,c++,開發(fā)語言,javascript,ajax

?

?

?model功能:提供對數(shù)據(jù)的操作(數(shù)據(jù)庫版)

#pragma once
//這個(gè)是mysql版本
/*
編號
標(biāo)題
難度
描述
時(shí)間(內(nèi)部),空間(內(nèi)部處理)

兩批文件構(gòu)成
1.question.list:題目列表:不需要出現(xiàn)題目描述
2.需要題目的描述,需要題目的預(yù)設(shè)置代碼(header.cpp),測試用例代碼(tail.cpp)

這兩個(gè)內(nèi)容是通過題目的編號,產(chǎn)生關(guān)聯(lián)的
*/
#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>

#include"include/mysql.h"
// 根據(jù)題目list文件,加載所有信息到內(nèi)存中
// model:主要用來和數(shù)據(jù)進(jìn)行交互,對外提供訪問數(shù)據(jù)的接口

namespace ns_model {
using namespace std;
using namespace ns_log;
using namespace ns_util;
class Question {
public:
  std::string number; // 題目編號
  std::string title;  // 題目的標(biāo)題
  std::string star;   // 難度:簡單中等困難
  int cpu_limit;      // 時(shí)間要求 s
  int mem_limit;      // 空間要求 kb
  std::string desc;   // 題目的描述
  std::string header; // 題目預(yù)設(shè)給用戶在線編輯器的代碼
  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:
  // 題號:題目細(xì)節(jié)
  unordered_map<string, Question> questions;
public:
  Model() { }
  bool QueryMySql(const std::string &sql,vector<Question> *out)
  {
    //創(chuàng)建mysql句柄
    MYSQL *my = mysql_init(nullptr);
    //連接數(shù)據(jù)庫
    if(mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0) == nullptr){
      LOG(FATAL)<<"連接數(shù)據(jù)庫失??!"<<"\n";
      return false;
    }
    //一定要設(shè)置該鏈接的編碼格式默認(rèn)是拉釘?shù)?    mysql_set_character_set(my,"utf8mb4");
    LOG(INFO)<<"連接數(shù)據(jù)庫成功"<<"\n";

    //執(zhí)行sql語句
    if(0 != mysql_query(my,sql.c_str()))
    {
      LOG(WARNING) << sql <<"execute error!"<<"\n";
      return false;
    }
    MYSQL_RES *res = mysql_store_result(my);
    //分析結(jié)果
    int rows = mysql_num_rows(res); //獲得行數(shù)量
    int cols = mysql_num_fields(res);//獲得列數(shù)量

    
    Question q;
    for(int i = 0;i<rows;i++)
    {
      MYSQL_ROW row = mysql_fetch_row(res);
      q.number = row[0];
      q.title = row[1];
      q.star = row[2];
      q.desc = row[3];
      q.header = row[4];
      q.tail = row[5];
      q.cpu_limit = atoi(row[6]);
      q.mem_limit = atoi(row[7]);

      out->push_back(q);
    }


    //釋放結(jié)果空間
    free(res);
    //關(guān)閉mysql連接
    mysql_close(my);

    return true;
  }
  bool GetAllQuestion(vector<Question> *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 ";
    sql+=oj_questions;
    sql+= " where number=";
    sql+=number;
    vector<Question> 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 ";
    sql+=oj_user;
    sql+=" where number=";
    sql+=number;

    //創(chuàng)建數(shù)據(jù)庫
    MYSQL *my = mysql_init(nullptr);
    //連接數(shù)據(jù)庫
    if(mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0) == nullptr)
    {
      LOG(WARNING)<<"連接到用戶數(shù)據(jù)庫失敗"<<"\n";
      return false;
    }
    //一定要記得設(shè)置該鏈接的編碼格式
    mysql_set_character_set(my,"utf8");
    LOG(INFO)<<"連接懂啊用戶數(shù)據(jù)庫成功"<<"\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)//獲得行數(shù)量
    { 
      //當(dāng)前輸入的數(shù)據(jù)可以創(chuàng)建用戶
      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出現(xiàn)錯(cuò)誤"<<"\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參數(shù)出錯(cuò)"<<"\n";
        mysql_stmt_close(stmt);
        mysql_close(my);
        return false;
      }

      //執(zhí)行插入語句
      if(mysql_stmt_execute(stmt)!=0){
        LOG(WARNING)<<"執(zhí)行stmt語句的時(shí)候出現(xiàn)錯(cuò)誤..."<<"\n";
        mysql_stmt_close(stmt);
        mysql_close(my);
        return false;
      }
      
      mysql_stmt_close(stmt);
      mysql_close(my);
      return true;
    }
    else{
      //服務(wù)器有重復(fù)的用戶num ,不允許再創(chuàng)建了
      return false;
    }
    //保存到服務(wù)器

    //這里out_json暫時(shí)沒有用,沒有要返回的值
    return true;
  }
  ~Model() {}
};
} // namespace ns_model

control:邏輯控制模塊

#pragma once

#include<iostream>
#include<string>
#include<cassert>
#include<algorithm>
#include<fstream>
#include<jsoncpp/json/json.h>
#include<vector>
#include<mutex>
#include"oj_view.hpp" 
// #include"oj_model.hpp"
#include"oj_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;
    //提供服務(wù)的主機(jī)的內(nèi)容
    class Machine
    {
    public:
        std::string ip; //編譯服務(wù)器的ip
        int port;       //編譯服務(wù)器的端口
        uint64_t load;  //編譯服務(wù)器負(fù)載
        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)<<"當(dāng)前ip:"<<ip<<"端口:"<<port<<"的load已經(jīng)清除load = "<<load<<"\n";
        if(mtx)mtx->unlock();
    }
    //提升主機(jī)負(fù)載
        void IncLoad()
        {
            if(mtx) mtx->lock();
            ++load;
            if(mtx) mtx->unlock();
        }
    //減少主機(jī)負(fù)載
        void DecLoad()
        {
            if(mtx) mtx->lock();
            --load;
            if(mtx) mtx->unlock();
        }

        //獲取主機(jī)負(fù)載,沒有太大的意義,只是為了同一接口
        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";
    //負(fù)載均衡模塊
    class LoadBalance
    { 
    private:
        //可以給我們提供編譯服務(wù)的所有的主機(jī)
        //每一臺(tái)主機(jī)都有自己的下標(biāo),充當(dāng)當(dāng)前主機(jī)的id
        std::vector<Machine> machines; 
        //所有在線的主機(jī)
        std::vector<int> online;
        //所有離線主機(jī)的id
        std::vector<int> offline;
        //保證選擇主機(jī)上的這個(gè)東西要保證數(shù)據(jù)安全
        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::vector<std::string> tokens;
                StringUtil::SplitString(line,&tokens,":");
                if(tokens.size()!=2)
                {
                    LOG(WARNING) <<"切分"<<line<<"失敗"<<"\n";
                    std::cout<<tokens[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:是一個(gè)輸出型參數(shù)
        //m:是一個(gè)輸出型參數(shù)
        bool SmartChoice(int *id,Machine **m)
        {
            //1.使用選擇好的主機(jī)(更新該主機(jī)的負(fù)載)
            //2.我們需要可能離線該主機(jī)
            mtx.lock();
            //選擇主機(jī)
            //一般的負(fù)載均衡的算法
            //1.隨機(jī)數(shù)法 + hash
            //2.輪詢 + hash
            int online_num = online.size();//在線主機(jī)的個(gè)數(shù)
            if(online_num == 0){
                mtx.unlock();
                LOG(FATAL) << "所有的后端編譯主機(jī)已經(jīng)全部離線,請后端的盡快重啟"<<"\n";
                return false;
            }
            LOG(DEBUG)<<"online:"<<online.size()<<"\n";
            //通過編譯,找到負(fù)載最小的機(jī)器
            *id = online[0];
            *m = &machines[online[0]];
            uint64_t min_load = machines[online[0]].Load();
            for(int i = 1;i<online_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){
                    //要離線的主機(jī)找到了
                    machines[which].ResetLoad();
                    LOG(DEBUG)<<"當(dāng)前離線主機(jī)的負(fù)載更改為:"<<machines[which].load;
                    online.erase(iter);
                    offline.push_back(which);
                    break;//因?yàn)閎reak的存在,所以暫時(shí)不考慮迭代器失效的問題
                }
            }
            mtx.unlock();
        }
        void OnlineMachine()
        {
            //我們統(tǒng)一上線,后面統(tǒng)一解決
            mtx.lock();
            
            online.insert(online.end(),offline.begin(),offline.end());
            offline.erase(offline.begin(),offline.end());
            mtx.unlock();
            LOG(INFO)<<"所有的主機(jī)又上線了"<<"\n";
            LOG(INFO)<<"online:"<<online.size()<<"offline"<<offline.size()<<"\n";
            

        }
        void ShowMachines()
        {
            mtx.lock();
            LOG(INFO)<<"online:"<<online.size()<<"offline"<<offline.size()<<"\n";
            mtx.unlock();
        }
    };

    //這是我們核心業(yè)務(wù)邏輯的控制器
    class Control
    {
    private:
        Model model_;//提供后臺(tái)數(shù)據(jù)
        View view_; //提供網(wǎng)頁渲染功能
        LoadBalance load_blance_; //核心負(fù)載均衡器
    public:
        void RecoveryMachine()
        {
            load_blance_.OnlineMachine();
        }
        //根據(jù)題目數(shù)據(jù)構(gòu)建網(wǎng)頁
        //html:輸出型參數(shù)
        bool AllQuestions(string *html)
        {
            bool ret = true;
            vector<Question> 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());
                });

                //獲取題目信息 成功,將所有的題目數(shù)據(jù)構(gòu)建成網(wǎng)頁
                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))
            {
                //獲取指定信息的題目成功,構(gòu)建程網(wǎng)頁
                view_.OneExpandHtml(q,html);
            }
            else
            {
                *html="獲取指定題目題目失敗,形成題目列表失敗";
                ret = false;
            }
            return ret;
        }
        void Login(const std::string in_json,std::string *out_json)
        {
            //in_json是發(fā)送過來的請求數(shù)據(jù),用戶的賬號等待
            //返回渲染的登錄界面
            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)<<"插入失敗,可能是重復(fù)的用戶"<<"\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)<<"調(diào)用Judge功能"<<"\n";
            // LOG(DEBUG)<<in_json<<"\nnumber:"<<number<<"\n";
            //0.根據(jù)題目編號,拿到題目細(xì)節(jié)
            Question q;
            model_.GetOneQuestion(number,&q);
            //1.in_json反序列化 ,得到題目的id,得到源代碼,input
            Json::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.選擇負(fù)載最低的主機(jī),然后發(fā)起HTTP請求得到結(jié)果
            //規(guī)則:一直選擇,直到主機(jī)可用,否則就是全部掛掉
            while(true)
            {
                int id = 0;
                Machine *m = nullptr;
                if(!load_blance_.SmartChoice(&id,&m))
                {   
                    break;
                }
                 //4.*out_json = 將結(jié)果復(fù)制給out_json
                Client cli(m->ip,m->port);
                m->IncLoad();
                LOG(DEBUG)<<"選擇主機(jī)成功,主機(jī)id:"<<id<<"\n詳情:"<<m->ip<<":"<<m->port<<"當(dāng)前主機(jī)負(fù)載:"<<m->Load()<<"\n";
                if(auto res = cli.Post("/compile_and_run",compile_string,"application/json;charset=utf-8"))
                {
                    //將我們的結(jié)果返回給out_json
                    if(res->status == 200)
                    {
                        *out_json = res->body;
                        m->DecLoad();
                        LOG(INFO)<<"請求編譯和運(yùn)行服務(wù)成功..."<<"\n";
                        break;
                    }                        
                    m->DecLoad();

                }
                else
                {
                    //請求失敗
                    LOG(ERROR)<<"選擇主機(jī)失敗,主機(jī)id:"<<id<<"詳情:"<<m->ip<<":"<<m->port<<"可能已經(jīng)離線"<<"\n";
                    load_blance_.OfflineMachine(id);
                    load_blance_.ShowMachines();//僅僅為了調(diào)試
                    
                }
                //m->DecLoad();
            }
           

        }
        Control(){}
        ~Control(){}
    };
}

control模塊實(shí)現(xiàn)了 負(fù)載均衡

  • 負(fù)載均衡
    • 第一種:隨機(jī)數(shù)+hash
    • 第二種:輪詢+hash , 本文是在用輪詢+hash
  • 為了實(shí)現(xiàn)負(fù)載均衡所有要把所有主機(jī)管理起來,有了Machine類
    • std::string ip :編譯服務(wù)器的ip
    • int port:編譯服務(wù)器的端口
    • uint64_t load :編譯服務(wù)器的負(fù)載
    • std::mutex *mtx:每個(gè)機(jī)器可能會(huì)同時(shí)被多個(gè)用戶訪問,所以要有鎖來保證臨界資源,并且mutex是不允許拷貝的,所以這里直接用指針,這樣在賦值構(gòu)造和拷貝構(gòu)造就沒事了

?

?view渲染功能:將后端的代碼渲染到html返回給前端

這里就要使用到ctemplate庫了:

基于負(fù)載均衡的在線OJ實(shí)戰(zhàn)項(xiàng)目,實(shí)戰(zhàn)項(xiàng)目,負(fù)載均衡,運(yùn)維,linux,c++,開發(fā)語言,javascript,ajax

?

?

#pragma once

#include<iostream>
#include<string>
#include<ctemplate/template.h>
// #include"oj_model.hpp"
#include"oj_model2.hpp"



namespace 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";
            //形成數(shù)據(jù)字典
            ctemplate::TemplateDictionary root("register");
            //獲取渲染的網(wǎng)頁
            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";
            //形成數(shù)據(jù)字典
            ctemplate::TemplateDictionary root("my_login");
            //獲取渲染網(wǎng)頁
            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);
            //開始渲染
            tpl->Expand(html,&root);
        }
        void AllExpandHtml(const vector<Question> &questions,std::string *html)
        {
            // 題目的編號 題目的標(biāo)題 題目的難度
            // 推薦使用表格顯示
            //1。形成路徑
            std::string src_html = template_path + "all_questions.html";
            LOG(INFO)<<"形成路徑成功:"<< src_html <<"\n";
            //2.形成數(shù)據(jù)字典
            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.獲取被渲染的網(wǎng)頁html
            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);
            LOG(INFO)<<"獲取渲染網(wǎng)頁的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
            //形成數(shù)字典
            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);
            //獲取被渲染的html
            ctemplate::Template *tpl = ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);
            //開始渲染功能
            tpl->Expand(html,&root);
        }
    };
}

?

?

總結(jié)?

1.前端的代碼在博客最上端綁定的文件當(dāng)中 ,篇幅太長不展示出來了

2.該項(xiàng)目的技術(shù)棧眾多,是c++后端和前端進(jìn)行交互的一個(gè)項(xiàng)目

3.項(xiàng)目的難點(diǎn)有:負(fù)載均衡的分配到每一臺(tái)編譯服務(wù)器、容錯(cuò)處理,能夠處理多種不同的錯(cuò)誤原因、并發(fā)處理要對臨界資源的管理、以及高并發(fā)訪問的話要對效率有所保證,畢竟在線oj服務(wù)是具有時(shí)效性的

4.debug困難,要在test.cc下測試成功后再進(jìn)行編寫,便于修改bug

?

?

?

?

?

?

?

?

?

?

?

?

?

?

到了這里,關(guān)于基于負(fù)載均衡的在線OJ實(shí)戰(zhàn)項(xiàng)目的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【項(xiàng)目實(shí)戰(zhàn)】Kafka 的 Leader 選舉和負(fù)載均衡

    【項(xiàng)目實(shí)戰(zhàn)】Kafka 的 Leader 選舉和負(fù)載均衡

    ?? 博主介紹 : 博主從事應(yīng)用安全和大數(shù)據(jù)領(lǐng)域,有8年研發(fā)經(jīng)驗(yàn),5年面試官經(jīng)驗(yàn),Java技術(shù)專家,WEB架構(gòu)師,阿里云專家博主,華為云云享專家,51CTO TOP紅人 Java知識(shí)圖譜點(diǎn)擊鏈接: 體系化學(xué)習(xí)Java(Java面試專題) ???? 感興趣的同學(xué)可以收藏關(guān)注下 , 不然下次找不到喲

    2024年02月16日
    瀏覽(19)
  • 【負(fù)載均衡oj】(七)ojserver

    負(fù)責(zé)負(fù)載均衡,獲取網(wǎng)站首頁,通過網(wǎng)絡(luò)羅調(diào)用編譯并運(yùn)行并提供結(jié)果給用戶。根據(jù)用戶的操作提供不同的功能。采用mvc結(jié)構(gòu)。使用 ctemplate文字模板做數(shù)據(jù)渲染 m在這里指的是和數(shù)據(jù)的交互,可以用文件或者sql交互。在使用文件的情況下,就必須要有對應(yīng)的文件目錄存放每一

    2024年01月24日
    瀏覽(20)
  • 【java蒼穹外賣項(xiàng)目實(shí)戰(zhàn)三】nginx反向代理和負(fù)載均衡

    【java蒼穹外賣項(xiàng)目實(shí)戰(zhàn)三】nginx反向代理和負(fù)載均衡

    我們思考一個(gè)問題: 前端發(fā)送的請求,是如何請求到后端服務(wù)的? 前端請求地址:http://localhost/api/employee/login 后端接口地址:http://localhost:8080/admin/employee/login 很明顯,兩個(gè)地址不一致,那是如何請求到后端服務(wù)的呢? 1、nginx反向代理 nginx 反向代理 ,就是將前端發(fā)送的動(dòng)態(tài)

    2024年02月21日
    瀏覽(27)
  • 【Linux】Linux+Nginx部署項(xiàng)目(負(fù)載均衡&動(dòng)靜分離)

    【Linux】Linux+Nginx部署項(xiàng)目(負(fù)載均衡&動(dòng)靜分離)

    接下來看看由輝輝所寫的關(guān)于Linux的相關(guān)操作吧 ? 目錄 ????Welcome Huihui\\\'s Code World ! !???? 一.Nginx負(fù)載均衡 1.什么是負(fù)載均衡 2.實(shí)現(xiàn)負(fù)載均衡的步驟 ①Nginx安裝 一鍵安裝4個(gè)依賴 解壓安裝包 進(jìn)入安裝包目錄 ②啟動(dòng) nginx 服務(wù) 進(jìn)入到指定目錄 啟動(dòng) 檢測是否成功啟動(dòng) ③開放防

    2024年02月06日
    瀏覽(47)
  • Nginx負(fù)載均衡 以及Linux前后端項(xiàng)目部署

    Nginx負(fù)載均衡 以及Linux前后端項(xiàng)目部署

    Nginx是一款高性能的開源Web服務(wù)器和反向代理服務(wù)器。它由俄羅斯的程序設(shè)計(jì)師Igor Sysoev創(chuàng)建,旨在解決傳統(tǒng)Web服務(wù)器的性能限制問題。 Nginx采用事件驅(qū)動(dòng)的架構(gòu)和異步非阻塞的處理方式,使其能夠處理大量并發(fā)連接,并具備良好的性能表現(xiàn)。它能夠處理靜態(tài)文件、索引文件以

    2024年02月06日
    瀏覽(25)
  • nginx配置負(fù)載均衡--實(shí)戰(zhàn)項(xiàng)目(適用于輪詢、加權(quán)輪詢、ip_hash)

    nginx配置負(fù)載均衡--實(shí)戰(zhàn)項(xiàng)目(適用于輪詢、加權(quán)輪詢、ip_hash)

    ????? 博主簡介 ????云計(jì)算領(lǐng)域優(yōu)質(zhì)創(chuàng)作者 ????華為云開發(fā)者社區(qū)專家博主 ????阿里云開發(fā)者社區(qū)專家博主 ?? 交流社區(qū): 運(yùn)維交流社區(qū) 歡迎大家的加入! ?? 希望大家多多支持,我們一起進(jìn)步!?? ??如果文章對你有幫助的話,歡迎 點(diǎn)贊 ???? 評論 ?? 收藏

    2024年02月08日
    瀏覽(20)
  • 【Linux】Nignx的入門使用&負(fù)載均衡&前端項(xiàng)目部署---超詳細(xì)

    【Linux】Nignx的入門使用&負(fù)載均衡&前端項(xiàng)目部署---超詳細(xì)

    ? ? ? ? ??Nginx是一個(gè)高性能的開源Web服務(wù)器和反向代理服務(wù)器。它使用事件驅(qū)動(dòng)的異步框架,可同時(shí)處理大量請求,支持負(fù)載均衡、反向代理、HTTP緩存等常見Web服務(wù)場景。Nginx可以作為一個(gè)前端的Web服務(wù)器,也可以作為反向代理服務(wù)器,將客戶端的請求轉(zhuǎn)發(fā)給后端的應(yīng)用服

    2024年02月06日
    瀏覽(31)
  • 【Linux】Nginx安裝使用負(fù)載均衡及動(dòng)靜分離(前后端項(xiàng)目部署),前端項(xiàng)目打包

    【Linux】Nginx安裝使用負(fù)載均衡及動(dòng)靜分離(前后端項(xiàng)目部署),前端項(xiàng)目打包

    ???????? Nginx 是一款高性能的 Web 服務(wù)器和 反向代理服務(wù)器 ,也可以充當(dāng)負(fù)載均衡器、HTTP 緩存和安全防護(hù)設(shè)備。它的特點(diǎn)是內(nèi)存占用小、穩(wěn)定性高、并發(fā)性強(qiáng)、易于擴(kuò)展,因此在互聯(lián)網(wǎng)領(lǐng)域得到了廣泛的使用。 總結(jié)出以下三點(diǎn): 負(fù)載均衡 :流量分?jǐn)?反向代理 :處理外

    2024年02月06日
    瀏覽(32)
  • Linux-----nginx的簡介,nginx搭載負(fù)載均衡以及nginx部署前后端分離項(xiàng)目

    Linux-----nginx的簡介,nginx搭載負(fù)載均衡以及nginx部署前后端分離項(xiàng)目

    目錄 nginx的簡介 是什么 nginx的特點(diǎn)以及功能 Nginx負(fù)載均衡 下載? 安裝? 負(fù)載均衡 Nginx 是一個(gè)高性能的開源Web服務(wù)器和反向代理服務(wù)器。它的設(shè)計(jì)目標(biāo)是為了解決C10k問題,即在同一時(shí)間內(nèi)支持上萬個(gè)并發(fā)連接。 Nginx采用事件驅(qū)動(dòng)的異步架構(gòu),能夠高效地處理大量并發(fā)請求,同

    2024年02月06日
    瀏覽(26)
  • 基于電商場景的高并發(fā)RocketMQ實(shí)戰(zhàn)-Consumer端隊(duì)列負(fù)載均衡分配機(jī)制、并發(fā)消費(fèi)以及消費(fèi)進(jìn)度提交

    基于電商場景的高并發(fā)RocketMQ實(shí)戰(zhàn)-Consumer端隊(duì)列負(fù)載均衡分配機(jī)制、并發(fā)消費(fèi)以及消費(fèi)進(jìn)度提交

    ???????????????? 【11來了】文章導(dǎo)讀地址:點(diǎn)擊查看文章導(dǎo)讀! ???????????????? Consumer 端隊(duì)列負(fù)載均衡分配機(jī)制 topic 是有一堆的 queue,而且分布在不同的 broker 上 并且在消費(fèi)時(shí),將多個(gè) queue 分配給多個(gè) consumer,每一個(gè) consumer 會(huì)分配到一部分的 queue 進(jìn)行消費(fèi)

    2024年02月03日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包