欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

目錄

柚子快報(bào)激活碼778899分享:運(yùn)維 實(shí)戰(zhàn)項(xiàng)目: 負(fù)載均衡

柚子快報(bào)激活碼778899分享:運(yùn)維 實(shí)戰(zhàn)項(xiàng)目: 負(fù)載均衡

http://yzkb.51969.com/

0. 前言

這個(gè)項(xiàng)目使用了前后端,實(shí)現(xiàn)一個(gè)丐版的LeetCode刷題網(wǎng)站,并根據(jù)每臺(tái)主機(jī)的實(shí)際情況,選擇對(duì)應(yīng)的主機(jī),負(fù)載均衡的調(diào)度?

0.1?所用技術(shù)與開發(fā)環(huán)境

所用技術(shù):??

C++ STL

標(biāo)準(zhǔn)庫(kù)

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

(

字符串切割

)

cpp-

httplib

第三方開源網(wǎng)絡(luò)庫(kù)

ctemplate 第三方開源前端網(wǎng)頁(yè)渲染庫(kù)

jsoncpp 第三方開源序列化、反序列化庫(kù)

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

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

MySQL C connect

Ace前端在線編輯器

(

部分

)

html/css/js/jquery/ajax (部分

)

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

Centos 7

云服務(wù)器,

vscode,?

Mysql Workbench

0.2 建立目錄及文件

0.3 項(xiàng)目宏觀結(jié)構(gòu)

具體的功能類似 leetcode 的題目列表+在線編程功能?

1.?compile?服務(wù)設(shè)計(jì)

?由于compiler這個(gè)模塊管理的是編譯與運(yùn)行,則可以先直接就創(chuàng)建所需要的文件

1.0 書寫makefile文件?

??隨著后續(xù)代碼的跟進(jìn),并不斷引入第三方庫(kù),這里還會(huì)新增編譯選項(xiàng)

1.1 compiler_server

1.1.0?編譯功能(compiler.hpp)

?在編譯的時(shí)候,無(wú)非存在2種情況,a)要么通過(guò),b)要么不通過(guò)要確定編譯通過(guò): 只需要確定是否生成對(duì)應(yīng)的.exe文件要當(dāng)編譯出錯(cuò)的時(shí)候(stderr): 需要將出錯(cuò)信息,重定向到一個(gè)臨時(shí)文件中,保存編譯出錯(cuò)的結(jié)果 還需要調(diào)用fork();子進(jìn)程完成編譯工作 父進(jìn)程繼續(xù)執(zhí)行

?由于需要頻繁的文件名轉(zhuǎn)換,所以在comm模塊中,新建util.hpp文件并將文件名轉(zhuǎn)換的函數(shù)放在一起

?

還有后面判斷編譯成功生成的可執(zhí)行程序,雖然可以直接暴力的打開文件判斷是否存在,但這里使用stat函數(shù)會(huì)好一些stat結(jié)構(gòu)體會(huì)記錄文件的各種信息

?注意: 程序替換是不會(huì)影響進(jìn)程的文件符描述符表的

1.1.1 日志模塊(log.hpp)

由于一般日志都會(huì)帶上時(shí)間,?這里還需要實(shí)現(xiàn)一個(gè)得到當(dāng)前時(shí)間的函數(shù),則我又在util.hpp把得到時(shí)間函數(shù)的類封裝成了一個(gè)類

?

由于會(huì)頻繁的調(diào)用日志進(jìn)行打印信息,也為了更簡(jiǎn)便的調(diào)用,我進(jìn)行了以下處理

?

如果在宏定義中使用#,那么這個(gè)宏就被稱為帶有字符串化操作的宏。這種宏可以將其參數(shù)轉(zhuǎn)換成字符串常量,并在預(yù)處理階段進(jìn)行替換。?

由于引入了日志,則就可以把之前所有的輸出信息,換成日志輸出?

1.1.2 測(cè)試編譯模塊?

?Compile的參數(shù)是文件名,它內(nèi)部會(huì)自動(dòng)拼接我們還需要再./temp中創(chuàng)建一個(gè)code.cpp文件

?上面我的代碼有一個(gè)錯(cuò)誤,在編譯成功的時(shí)候,并沒(méi)有return,導(dǎo)致LOG日志打印有問(wèn)題

?要是我們的源文件有問(wèn)題,錯(cuò)誤信息就會(huì)重定向到 文件.compile_error中在測(cè)試的時(shí)候,還需要把 文件.exe 文件.compile_error文件刪除,就是上次生成的文件

1.1.3?運(yùn)行功能(runner.hpp)?

程序運(yùn)行: 1)代碼跑完,結(jié)果正確 2)代碼跑完,結(jié)果不正確, 3)代碼沒(méi)跑完,異常了

程序結(jié)果是否正確,是由oj_server中的測(cè)試用例決定的,則run模塊只考慮是否正確運(yùn)行完畢

#include

#include

#include

#include

#include

#include // fork接口需要

#include "../comm/log.hpp"

#include "../comm/util.hpp"

namespace ns_runner

{

using namespace ns_util;

using namespace ns_log;

class Runner

{

public:

Runner(){}

~Runner(){}

static int Run(const std::string &file_name){

std::string _execute = PathUtil::Exe(file_name);// 可執(zhí)行

std::string _stdin = PathUtil::Stdin(file_name);// 輸入

std::string _stdout = PathUtil::Stdout(file_name);// 輸出

std::string _stderr = PathUtil::Stderr(file_name);// 錯(cuò)誤

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)行創(chuàng)建子進(jìn)程失敗" << "\n";

close(_stdin_fd);

close(_stdout_fd);

close(_stderr_fd);

return -2;// 代表創(chuàng)建自己失敗

}

else if(pid == 0){

// 子進(jìn)程

dup2(_stdin_fd,0);

dup2(_stdout_fd,1);

dup2(_stderr_fd,2);

LOG(INFO) << "123";// 是不是有問(wèn)題啊

// 這個(gè)程序替換等價(jià)于 ./tmp/code.exe ./tmp/code.exe

execl(_execute.c_str(),_execute.c_str(),nullptr);

exit(1);

}

else{

close(_stdin_fd);

close(_stdout_fd);

close(_stderr_fd);

int status = 0;// 表示輸出型參數(shù)

waitpid(pid,&status,0);// 阻塞式等待

// 程序運(yùn)行異常,一定是因?yàn)槭盏叫盘?hào)

LOG(INFO) << "運(yùn)行完畢,infor: " << (status & 0x7f) << "\n";

return status & 0x7f;

}

}

};

}

返回值 > 0: 程序異常了,退出時(shí)收到了信號(hào),返回值就是對(duì)應(yīng)的信號(hào)編號(hào)返回值 == 0: 正常運(yùn)行完畢的,結(jié)果保存到了對(duì)應(yīng)的臨時(shí)文件中返回值 < 0: 內(nèi)部錯(cuò)誤 run.hpp也是一樣的,把自己的各種輸出信息,輸出到一個(gè)臨時(shí)文件中要判斷一個(gè)程序是否異常,只需要看它是否收到了異常信號(hào)

解釋waitpid第2個(gè)輸出型參數(shù)?

?

?status并不是按照整數(shù)來(lái)整體使用的,而是按照比特位的方式,將32個(gè)比特位進(jìn)行劃分,只需要學(xué)習(xí)低16位這也是上面為什么會(huì)寫成status & 0x7F的原因

那6個(gè)程序替換的系統(tǒng)接口,具體使用那個(gè)看實(shí)際情況?

沒(méi)有p就需要帶路徑有l(wèi),就是列表式傳命令有v就是數(shù)組式傳命令有e就需要傳自己設(shè)置的環(huán)境變量

1.1.4 測(cè)試運(yùn)行模塊

雖然運(yùn)行模塊已經(jīng)能正常運(yùn)行了,但是萬(wàn)一code.cpp是惡意程序了,比如死循環(huán),不停消耗CPU資源 , 所以還需要進(jìn)一步的資源約束

1.1.5 添加資源限制(setrlimit)

資源不足,導(dǎo)致OS終止進(jìn)程,是通過(guò)信號(hào)終止的 ?

?

?為了方便上層調(diào)用,我直接在Run函數(shù)中增加了cpu_limit和mem_limit形參

?這個(gè)項(xiàng)目走到這里就需要編寫compile_run.hpp,將編譯和運(yùn)行的邏輯連接在一起,且code.cpp需要被處理的源文件,不應(yīng)該是我們自己添加的,而是需要再客戶端中導(dǎo)入的

1.1.6 編譯 && 運(yùn)行功能 (compile_run.hpp)

這個(gè)模塊要做的是:

a)適配用戶請(qǐng)求,引入json定制通信協(xié)議字段

b)形成唯一文件名

c)正確調(diào)用compile and run

在centos中安裝: sudo yum install json-c-devel

頭文件 #include

?注意: 在編譯引入了json的文件,需要加上-ljsoncpp

?

雖然這個(gè)code就是文件名了,但client可能會(huì)提交大量的代碼,所以內(nèi)部就會(huì)需要形成唯一的文件名(待完善)還有很多個(gè)出錯(cuò)問(wèn)題怎么解決(待完善)

?complie_run.hpp

#pragma once

#include "compiler.hpp"

#include "runner.hpp"

#include "../comm/log.hpp"

#include "../comm/util.hpp"

#include

namespace ns_compile_and_run

{

using namespace ns_log;

using namespace ns_util;

using namespace ns_compiler;

using namespace ns_runner;

// 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){

// step1: 反序列化過(guò)程

Json::Value in_value;

Json::Reader reader;

// 把in_json中的數(shù)據(jù)寫到in_value中

reader.parse(in_json,in_value);// 最后再處理差錯(cuò)問(wèn)題

std::string code = in_value["code"].asString();

std::string input = in_value["input"].asString();

int cpu_limit = in_value["cpu_limit"].asInt();

int mem_limit = in_value["mem_limit"].asInt();

Json::Value out_value;

int status_code = 0;

int run_result = 0;

std::string file_name;// 唯一文件名

if(code.size() == 0){

status_code = -1;// 代碼為空

goto END;

}

// 形成的文件名只居有唯一性,沒(méi)有目錄沒(méi)有后綴

// 使用: 毫秒級(jí)時(shí)間戳 + 原子性遞增唯一值 : 來(lái)保證唯一性

file_name = FileUtil::UniqFileName();

// 形成臨時(shí)的src文件

if(!FileUtil::WriteFile(PathUtil::Src(file_name),code)){

status_code = -2;// 未知錯(cuò)誤

goto END;

}

if(!Compiler::Compile(file_name)){

status_code = -3;// 編譯錯(cuò)誤

goto END;

}

run_result = Runner::Run(file_name,cpu_limit,mem_limit);

if(run_result < 0){

// runnem模塊內(nèi)部錯(cuò)誤

status_code = -2;// 未知錯(cuò)誤

}

else if(run_result > 0){

// 程序運(yùn)行崩潰

status_code = run_result;// 這里的run_result是信號(hào)

}

else{

// 運(yùn)行成功

status_code = 0;

}

END:

out_value["status"] = status_code;

out_value["reason"] = CodeToDest(status_code,file_name);// 得到錯(cuò)誤信息字符串

if(status_code == 0){

// 整個(gè)過(guò)程全部成功

std::string _stdout;

FileUtil::ReadFile(PathUtil::Stdout(file_name),&_stdout,true);

out_value["stdout"] = _stdout;

std::string _stderr;

FileUtil::ReadFile(PathUtil::Stdout(file_name),&_stderr,true);

out_value["stdout"] = _stdout;

}

// step2: 序列化

Json::StyledWriter writer;

*out_json = writer.write(out_value);

}

}

?1.1.7 基于compile_run.hpp對(duì)util.hpp的補(bǔ)充

?

注意引入流時(shí)需要引入頭文件: #include ? getline:不保存行分割符,有些時(shí)候需要保留\n,getline內(nèi)部重載了強(qiáng)制類型轉(zhuǎn)化?

1.1.8 測(cè)試編譯運(yùn)行模塊?

#include "compile_run.hpp"

using namespace ns_compile_and_run;

int main()

{

std::string in_json;

Json::Value in_value;

// R"()", raw string

in_value["code"] = R"(#include

int main(){

std::cout << "你可以看見我了" << std::endl;

return 0;

})";

in_value["input"] = "";

in_value["cpu_limit"] = 1;

in_value["mem_limit"] = 10240 * 3;

Json::FastWriter writer;

in_json = writer.write(in_value);

// std::cout << in_json << std::endl;

// 這個(gè)是將來(lái)給客戶端返回的json串

std::string out_json;

CompileAndRun::Start(in_json, &out_json);

std::cout << out_json << std::endl;

return 0;

}

?實(shí)際上這里的代碼應(yīng)該是client自動(dòng)提交給我們的,我們直接使用第三方庫(kù)就行了待優(yōu)化: 可以把臨時(shí)生成的這些文件都清理掉,

1.1.9 清理臨時(shí)文件

?這個(gè)函數(shù)直接放在compile_server.cc中的start函數(shù)的最后,清理臨時(shí)文件

1.1.10 引入cpp-httplib 網(wǎng)絡(luò)庫(kù)

?下載地址:?cpp-httplib: C++ http 網(wǎng)絡(luò)庫(kù) - Gitee.com

?這個(gè)就是別人寫好的網(wǎng)絡(luò)庫(kù),我們直接使用就行了

1.1.11 更新gcc

安裝scl :?sudo yum install centos-release-scl scl-utils-build

安裝新版本gcc:?

sudo yum install

-

y devtoolset

-

9

-

gcc devtoolset

-

9

-

gcc

-

c

++

把?scl enable devtoolset-9 bash 放在?~/.bash_profile中想每次登陸的時(shí)候,都是較新的gcc

?如果不更新在使用cpp-httplib時(shí)可能會(huì)報(bào)錯(cuò),?用老的編譯器,要么編譯不通過(guò),要么直接運(yùn)行報(bào)錯(cuò)

1.1.12 測(cè)試cpp-httplib網(wǎng)絡(luò)庫(kù)?

可能會(huì)出現(xiàn)服務(wù)器的公網(wǎng)ip無(wú)法訪問(wèn)的問(wèn)題,可以試試把防火墻關(guān)閉,并打開端口

1.1.13 將compiler_server打包成網(wǎng)絡(luò)服務(wù)

compiler_server.cc

#include "compile_run.hpp"

#include "../comm/httplib.h"http:// 引入

using namespace ns_compile_and_run;

using namespace httplib;// 引入

void Usage(std::string proc){

std::cerr << "Usage: " << "\n\t" << proc << " port" << std::endl;

}

//./copile_server port

int main(int argc,char *argv[])

{

if(argc != 2){

Usage(argv[0]);

return 1;

}

Server svr;

svr.Post("/compile_and_run",[](const Request&req,Response & resp){

// 用戶請(qǐng)求的服務(wù)正文是我們想要的json string

std::string in_json = req.body;

std::string out_json;

if(!in_json.empty()){

CompileAndRun::Start(in_json,&out_json);

resp.set_content(out_json,"application/json;charset=uft-8");

}

});

svr.listen("0.0.0.0",atoi(argv[1]));

return 0;

}

由于我這里沒(méi)有寫客戶端代碼,則這里暫時(shí)不好測(cè)試,不過(guò)可以借助第三方工具進(jìn)行測(cè)試

?2.?oj_server服務(wù)設(shè)計(jì)

本質(zhì):

建立一個(gè)小型網(wǎng)站

1. 獲取首頁(yè),用題目列表充當(dāng)

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

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

2.1 書寫makefile文件

?隨著后續(xù)代碼的跟進(jìn),并不斷引入第三方庫(kù),這里還會(huì)新增編譯選項(xiàng)

2.2?服務(wù)路由功能(oj_server.cc)?

為用戶實(shí)現(xiàn)的路由功能就3個(gè) a. 獲取所有的題目列表 b.根據(jù)題目編號(hào),獲取題目?jī)?nèi)容 c.判斷用戶提交的代碼??

2.3 MVC 結(jié)構(gòu)的oj 服務(wù)設(shè)計(jì)(M)

Model

,

通常是和數(shù)據(jù)交互的模塊

,比如,對(duì)題庫(kù)進(jìn)行增刪改查(文件版,

MySQL

2.3.1?安裝boost庫(kù) && 字符切分功能

sudo yum install -y boost-devel //是boost 開發(fā)庫(kù)

?第一個(gè)參數(shù)為緩沖區(qū),第二個(gè)參數(shù)為被分割的字符串第三個(gè)參數(shù)為分割符,第四個(gè)參數(shù)為是否壓縮

要壓縮: 當(dāng)sep = "空格"時(shí),sepsepsep?-> 空格不壓縮: 當(dāng)sep = "空格"時(shí),sepsepsep -> 空格空格空格

?2.3.2 數(shù)據(jù)結(jié)構(gòu)

header.cpp

#include

#include

#include

#include

#include

using namespace std;

class Solution{

public:

bool isPalindrome(int x)

{

//將你的代碼寫在下面

return true;

}

};

tail.cpp

#ifndef COMPILER_ONLINE

#include "header.cpp"

#endif

// 這里先把測(cè)試用例 暴露出來(lái)

void Test1()

{

// 通過(guò)定義臨時(shí)對(duì)象,來(lái)完成方法的調(diào)用

bool ret = Solution().isPalindrome(121);

if(ret){

std::cout << "通過(guò)用例1, 測(cè)試121通過(guò) ... OK!" << std::endl;

}

else{

std::cout << "沒(méi)有通過(guò)用例1, 測(cè)試的值是: 121" << std::endl;

}

}

void Test2()

{

// 通過(guò)定義臨時(shí)對(duì)象,來(lái)完成方法的調(diào)用

bool ret = Solution().isPalindrome(-10);

if(!ret){

std::cout << "通過(guò)用例2, 測(cè)試-10通過(guò) ... OK!" << std::endl;

}

else{

std::cout << "沒(méi)有通過(guò)用例2, 測(cè)試的值是: -10" << std::endl;

}

}

int main()

{

Test1();

Test2();

return 0;

}

des.txt表示題目信息?header.cpp表示預(yù)設(shè)代碼tail.cpp表示測(cè)試用例

?真正代碼 = 用戶在head.cpp中的代碼 + header.cpp + tail.cpp 并去到COMPILER_ONLINE?這個(gè)條件編譯只是為了編寫tail.cpp時(shí)不報(bào)錯(cuò)

2.3.3?model功能(oj_model.cpp)

數(shù)據(jù)交互 && 提供接口

?

#pragma once

// 文件版本

#include "../comm/util.hpp"

#include "../comm/log.hpp"

#include

#include

#include

namespace ns_model

{

using namespace std;

using namespace ns_log;

using namespace ns_util;

struct Question{

string number;// 題目編號(hào),唯一

string tile;// 題目標(biāo)題

string star;// 難度: 簡(jiǎn)單 中等 困難

int cpu_limit;// 題目的時(shí)間復(fù)雜度(S)

int mem_limit;// 題目的空間復(fù)雜度(KB)

string desc;// 題目描述

string header; // 題目預(yù)設(shè)給用戶在線編輯器的代碼

string tail;// 題目測(cè)試用例,需要和header拼接

};

const string questions_list = "./question/quetions.list";

const string questions_path = "./question";

class Model

{

public:

Model(){

// 加載所有題目:底層是用hash表映射的

assert(LoadQuestionList(questions_list));

}

~Model(){

;

}

// 獲取所有題目,這里的out是輸出型參數(shù)

bool GetAllQuestions(vector*out){

if(questions.size() == 0){

LOG(ERROR) << "用戶獲取題庫(kù)失敗" << "\n";

return false;

}

for(const auto&q: questions){

out->push_back(q.second);

}

return true;

}

// 獲取指定題目,這里的q是輸出型參數(shù)

bool GetOneQuestion(const string& number,Question* q){

const auto& iter = questions.find(number);

if(iter == questions.end()){

LOG(ERROR) << "用戶獲取題目失敗,題目編號(hào): " << number << "\n";

return false;

}

(*q) = iter->second;

return true;

}

// 加載配置文件: questions/questions.list + 題目編號(hào)文件

bool LoadQuestionList(const string&question_list){

// 加載配置文件: questions/questions.list +題目編號(hào)文件

ifstream in(question_list);

if(!in.is_open()){

LOG(FATAL) << "加載題庫(kù)失敗,請(qǐng)檢查是否存在題庫(kù)文件" << "\n";

return false;

}

string line;

while(getline(in,line)){

vectortokens;

StringUtil::SplitString(line,&tokens," ");// 被分割的字符串 緩沖區(qū) 分割符

// eg: 1 判斷回文數(shù) 簡(jiǎn)單 1 30000

if(tokens.size()!=5){

LOG(WARNING) << "加載部分題目失敗,請(qǐng)檢查文件格式" << "\n";

continue;

}

Question q;

q.number = tokens[0];

q.tile = tokens[1];

q.star = tokens[2];

q.cpu_limit = atoi(tokens[3].c_str());

q.mem_limit = atoi(tokens[4].c_str());

string path = questions_list;

path += q.number;

path += "/";

// 第三個(gè)參數(shù)代表 是否加上 \n

FileUtil::ReadFile(path+"desc.txt",&(q.desc),true);

FileUtil::ReadFile(path+"header.cpp",&(q.header),true);

FileUtil::ReadFile(path+"tail.txt",&(q.tail),true);

questions.insert({q.number,q});// 錄題成功

}

LOG(INFO) << "加載題庫(kù)...成功" << "\n";

in.close();

}

private:

// 題號(hào) : 題目細(xì)節(jié)

unordered_map questions;

};

}

2.4 MVC 結(jié)構(gòu)的oj 服務(wù)設(shè)計(jì)(C)?

2.4.1 負(fù)載均衡模塊

namespace ns_control

{

using namespace std;

using namespace ns_log;

using namespace ns_util;

using namespace ns_model;

using namespace ns_view;

using namespace httplib;

// 提供服務(wù)的主機(jī)

class Machine

{

public:

std::string ip; //編譯服務(wù)的ip

int port; //編譯服務(wù)的port

uint64_t load; //編譯服務(wù)的負(fù)載

std::mutex *mtx; // mutex禁止拷貝的,使用指針

public:

Machine() : ip(""), port(0), load(0), mtx(nullptr)

{

}

~Machine()

{

}

public:

// 提升主機(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();

}

void ResetLoad()

{

if(mtx) mtx->lock();

load = 0;

if(mtx) mtx->unlock();

}

// 獲取主機(jī)負(fù)載,沒(méi)有太大的意義,只是為了統(tǒng)一接口

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 LoadBlance

{

private:

// 可以給我們提供編譯服務(wù)的所有的主機(jī)

// 每一臺(tái)主機(jī)都有自己的下標(biāo),充當(dāng)當(dāng)前主機(jī)的id

std::vector machines;

// 所有在線的主機(jī)id

std::vector online;

// 所有離線的主機(jī)id

std::vector offline;

// 保證LoadBlance它的數(shù)據(jù)安全

std::mutex mtx;

public:

LoadBlance()

{

assert(LoadConf(service_machine));

LOG(INFO) << "加載 " << service_machine << " 成功"

<< "\n";

}

~LoadBlance()

{

}

public:

bool LoadConf(const std::string &machine_conf)

{

std::ifstream in(machine_conf);

if (!in.is_open())

{

LOG(FATAL) << " 加載: " << machine_conf << " 失敗"

<< "\n";

return false;

}

std::string line;

while (std::getline(in, line))

{

std::vector tokens;

StringUtil::SplitString(line, &tokens, ":");

if (tokens.size() != 2)

{

LOG(WARNING) << " 切分 " << line << " 失敗"

<< "\n";

continue;

}

Machine m;

m.ip = tokens[0];

m.port = atoi(tokens[1].c_str());

m.load = 0;

m.mtx = new std::mutex();

online.push_back(machines.size());

machines.push_back(m);

}

in.close();

return true;

}

// id: 輸出型參數(shù)

// m : 輸出型參數(shù)

bool SmartChoice(int *id, Machine **m)

{

// 1. 使用選擇好的主機(jī)(更新該主機(jī)的負(fù)載)

// 2. 我們需要可能離線該主機(jī)

mtx.lock();

// 負(fù)載均衡的算法

// 1. 隨機(jī)數(shù)+hash

// 2. 輪詢+hash

int online_num = online.size();

if (online_num == 0)

{

mtx.unlock();

LOG(FATAL) << " 所有的后端編譯主機(jī)已經(jīng)離線, 請(qǐng)運(yùn)維的同事盡快查看"

<< "\n";

return false;

}

// 通過(guò)遍歷的方式,找到所有負(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)

{

machines[which].ResetLoad();

//要離線的主機(jī)已經(jīng)找到啦

online.erase(iter);

offline.push_back(which);

break; //因?yàn)閎reak的存在,所有我們暫時(shí)不考慮迭代器失效的問(wèn)題

}

}

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";

}

//for test

void ShowMachines()

{

mtx.lock();

std::cout << "當(dāng)前在線主機(jī)列表: ";

for(auto &id : online)

{

std::cout << id << " ";

}

std::cout << std::endl;

std::cout << "當(dāng)前離線主機(jī)列表: ";

for(auto &id : offline)

{

std::cout << id << " ";

}

std::cout << std::endl;

mtx.unlock();

}

};

}

2.4.1?control功能(oj_control.hpp)

邏輯控制模塊

// 這是我們的核心業(yè)務(wù)邏輯的控制器

class Control

{

private:

Model model_; //提供后臺(tái)數(shù)據(jù)

View view_; //提供html渲染功能

LoadBlance load_blance_; //核心負(fù)載均衡器

public:

Control()

{

}

~Control()

{

}

public:

void RecoveryMachine()

{

load_blance_.OnlineMachine();

}

//根據(jù)題目數(shù)據(jù)構(gòu)建網(wǎng)頁(yè)

// html: 輸出型參數(shù)

bool AllQuestions(string *html)

{

bool ret = true;

vector all;

if (model_.GetAllQuestions(&all))

{

sort(all.begin(), all.end(), [](const struct Question &q1, const struct Question &q2){

return atoi(q1.number.c_str()) < atoi(q2.number.c_str());

});

// 獲取題目信息成功,將所有的題目數(shù)據(jù)構(gòu)建成網(wǎng)頁(yè)

// ...

}

else

{

*html = "獲取題目失敗, 形成題目列表失敗";

ret = false;

}

return ret;

}

bool Question(const string &number, string *html)

{

bool ret = true;

struct Question q;

if (model_.GetOneQuestion(number, &q))

{

// 獲取指定題目信息成功,將所有的題目數(shù)據(jù)構(gòu)建成網(wǎng)頁(yè)

// ....

}

else

{

*html = "指定題目: " + number + " 不存在!";

ret = false;

}

return ret;

}

// code: #include...

// input: ""

void Judge(const std::string &number, const std::string in_json, std::string *out_json)

{

}

};

?control模塊中的判題功能,我打算最后設(shè)計(jì)

2.5??MVC 結(jié)構(gòu)的oj 服務(wù)設(shè)計(jì)(V)

2.4?安裝與測(cè)試 ctemplate(網(wǎng)頁(yè)渲染)

渲染本質(zhì)就是key-value之間的替換

安裝鏡像源: git clone https://gitee.com/mirrors_OlafvdSpek/ctemplate.git?

.

/

autogen

.

sh

.

/

configure

make

//

編譯 如果報(bào)錯(cuò)請(qǐng)

更新gcc

make install

test.cpp?

#include

#include

#include

int main()

{

std::string html = "./test.html";

std::string html_info = "測(cè)試ctemplate渲染";

// 建立ctemplate參數(shù)目錄結(jié)構(gòu)

ctemplate::TemplateDictionary root("test"); // unordered_map test;

// 向結(jié)構(gòu)中添加你要替換的數(shù)據(jù),kv的

root.SetValue("info", html_info); // test.insert({key, value});

// 獲取被渲染對(duì)象

// DO_NOT_STRIP:保持html網(wǎng)頁(yè)原貌

ctemplate::Template *tpl = ctemplate::Template::GetTemplate(html,ctemplate::DO_NOT_STRIP);

// 開始渲染,返回新的網(wǎng)頁(yè)結(jié)果到out_html

std::string out_html;

tpl->Expand(&out_html, &root);

std::cout << "渲染的帶參html是:" << std::endl;

std::cout << out_html << std::endl;

return 0;

}

?test.html

Document

{{info}}

{{info}}

{{info}}

{{info}}

?錯(cuò)誤原因: error while loading shared libraries: libmpc.so.3: cannot open shared object file?

export LD\_LIBRARY\_PATH=$LD\_LIBRARY\_PATH:/usr/local/lib

?在命令行上輸入 上面這段命令,注:只在當(dāng)前會(huì)話中有效

# ?cat /etc/ld.so.conf include ld.so.conf.d/*.conf # ?echo "/usr/local/lib" >> /etc/ld.so.conf # ?ldconfig

2.5.2 渲染功能(oj_view.hpp)

#pragma once

#include

#include

#include "./oj_model.hpp"

namespace ns_view

{

using namespace ns_model;

const std::string template_path = "./template_html/";

class View

{

public:

View(){}

~View(){}

// 渲染所有題目

void ALLExpandHtml(const vector&question,std::string *html){

// 題目編號(hào) 題目標(biāo)題 題難度

// 推薦表格實(shí)現(xiàn)

// 1.形成路徑

string src_html = template_path + "all_quetions.html";

// 2.形成數(shù)字典

ctemplate::TemplateDictionary root("all_question");

for(const auto& q: question){

ctemplate::TemplateDictionary *sub = root.AddSectionDictionary("question_list");

sub->SetValue("number",q.number);

sub->SetValue("title",q.title);

sub->SetValue("star",q.star);

}

// 3. 獲取被渲染的html

ctemplate::Template*tpl = ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);

// 4.開始完成渲染功能

tpl->Expand(html,&root);

}

// 渲染一道題目

void OneExpandHtml(const struct Question &q,string *html){

// 1.形成路徑

std::string src_html = template_path + "one_question.html";

// 2. 形成數(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("header",q.header);

// 3.獲取被渲染的html

ctemplate::Template*tpl = ctemplate::Template::GetTemplate(src_html,ctemplate::DO_NOT_STRIP);

// 4.開始完成渲染功能

tpl->Expand(html,&root);

}

};

}

2.6? 聯(lián)動(dòng)MVC模塊并測(cè)試

oj_server.cc?

#include

#include "../comm/httplib.h"http:// 引入

#include "oj_control.hpp"

using namespace httplib;// 引入

using namespace ns_control;

int main()

{

// 用戶請(qǐng)求的服務(wù)器路由功能

Server svr;

Control ctrl;

// 獲取所有的題目列表

svr.Get("/all_questions",[&ctrl](const Request&req,Response &resp){

// 返回一張包含所有題目的html網(wǎng)頁(yè)

std::string html;// 待處理

ctrl.AllQuestions(&html);

resp.set_content(html,"text/html;charset=utf-8");

});

// 根據(jù)題目編號(hào),獲取題目?jī)?nèi)容

// \d+ 是正則表達(dá)式的特殊符合

svr.Get(R"(/question/(\d+))",[&ctrl](const Request&req,Response &resp){

std::string number = req.matches[1];

std::string html;

ctrl.Question(number,&html);

resp.set_content(html,"text/html;charset=utf-8");

});

// 判斷用戶提交的代碼(1.每道題c測(cè)試用例,2.compile_and_run)

svr.Post(R"(/judge/(\d+))",[&ctrl](const Request&req,Response &resp){

std::string number = req.matches[1];

std::string result_json;

ctrl.Judge(number,req.body,&result_json);

resp.set_content(result_json,"application/json;charset=utf-8");

});

svr.set_base_dir("./wwwroot");

svr.listen("0.0.0.0",8080);

return 0;

}

?這里的前端都是提前做好了的,我們可以不關(guān)心前端;control功能還有個(gè)判題功能沒(méi)有實(shí)現(xiàn)

?2.7 完善oj_control.hpp中的判題功能

void Judge(const std::string &number, const std::string in_json, std::string *out_json)

{

// LOG(DEBUG) << in_json << " \nnumber:" << number << "\n";

// 0. 根據(jù)題目編號(hào),直接拿到對(duì)應(yīng)的題目細(xì)節(jié)

struct Question q;

model_.GetOneQuestion(number, &q);

// 1. in_json進(jìn)行反序列化,得到題目的id,得到用戶提交源代碼,input

Json::Reader reader;

Json::Value in_value;

reader.parse(in_json, in_value);

std::string code = in_value["code"].asString();

// 2. 重新拼接用戶代碼+測(cè)試用例代碼,形成新的代碼

Json::Value compile_value;

compile_value["input"] = in_value["input"].asString();

compile_value["code"] = code + "\n" + q.tail;

compile_value["cpu_limit"] = q.cpu_limit;

compile_value["mem_limit"] = q.mem_limit;

Json::FastWriter writer;

std::string compile_string = writer.write(compile_value);

// 3. 選擇負(fù)載最低的主機(jī)(差錯(cuò)處理)

// 規(guī)則: 一直選擇,直到主機(jī)可用,否則,就是全部掛掉

while(true)

{

int id = 0;

Machine *m = nullptr;

if(!load_blance_.SmartChoice(&id, &m))

{

break;

}

// 4. 然后發(fā)起http請(qǐng)求,得到結(jié)果

Client cli(m->ip, m->port);

m->IncLoad();

LOG(INFO) << " 選擇主機(jī)成功, 主機(jī)id: " << id << " 詳情: " << 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"))

{

// 5. 將結(jié)果賦值給out_json

if(res->status == 200)

{

*out_json = res->body;

m->DecLoad();

LOG(INFO) << "請(qǐng)求編譯和運(yùn)行服務(wù)成功..." << "\n";

break;

}

m->DecLoad();

}

else

{

//請(qǐng)求失敗

LOG(ERROR) << " 當(dāng)前請(qǐng)求的主機(jī)id: " << id << " 詳情: " << m->ip << ":" << m->port << " 可能已經(jīng)離線"<< "\n";

load_blance_.OfflineMachine(id);

load_blance_.ShowMachines(); //僅僅是為了用來(lái)調(diào)試

}

}

2.8 測(cè)試oj_server服務(wù)

在編譯時(shí)需要加上-D COMPILER_ONLINE條件編譯,?

?設(shè)計(jì)到前端網(wǎng)頁(yè),下面會(huì)有提及

2.9 一個(gè)BUG?

?把tail.txt改成tail.cpp,不然后面無(wú)法進(jìn)行代碼拼接

?3. 前端頁(yè)面設(shè)計(jì)(了解)

3.1 index.html

這是我的個(gè)人OJ系統(tǒng)

歡迎來(lái)到我的OnlineJudge平臺(tái)

這個(gè)我個(gè)人獨(dú)立開發(fā)的一個(gè)在線OJ平臺(tái)

點(diǎn)擊我開始編程啦!

?3.2?all_questions.html

在線OJ-題目列表

OnlineJuge題目列表

{{#question_list}}

{{/question_list}}

編號(hào)標(biāo)題難度
{{number}}{{title}}{{star}}

3.3 one_questions.html(ACE插件&&JQuery&&ajax)

ACE插件是一個(gè)

編寫代碼的編譯框

收集當(dāng)前頁(yè)面的有關(guān)數(shù)據(jù)

, a.

題號(hào)

a.

代碼

,

我們采用

JQuery

來(lái)進(jìn)行獲取

html

中的內(nèi)容

構(gòu)建json,并通過(guò)

ajax向后臺(tái)

發(fā)起基于http的json請(qǐng)求

全部代碼

{{number}}.{{title}}

{{number}}.{{title}}_{{star}}

{{desc}}

3.4 相關(guān)測(cè)試

?4. MySQL版題目設(shè)計(jì)

4.1 注冊(cè)用戶 && 賦予權(quán)限

create user 'oj_client'@'localhost' identified by '123456';create database oj;grant select on oj.* to 'oj_client'@'localhost';select user,Host from user;

?4.2 下載第三方工具-workbench

?

?下載下來(lái)之后,就不斷的下一步,下一步就行了

4.3 錄題到mysql中

use oj;

drop table if exists oj_table;

create table if not exists oj_table(

_number varchar(200) comment '題目編號(hào)',

_titie varchar(200) comment '題目標(biāo)題',

_start varchar(200) comment '題目簡(jiǎn)單中等困難',

_desc varchar(2000) comment '題目描述',

_header varchar(2000) comment '題目預(yù)設(shè)',

_tail varchar(2000) comment '題目測(cè)試用例',

_cpu_limit int comment '時(shí)間要求',

_mem_limt int comment '空間要求'

);

insert into oj_table values(

1,

'判斷回文數(shù)',

'簡(jiǎn)單',

'判斷一個(gè)整數(shù)是否是回文數(shù)?;匚臄?shù)是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數(shù)。

示例 1:

輸入: 121

輸出: true

示例 2:

輸入: -121

輸出: false

解釋: 從左向右讀, 為 -121 。 從右向左讀, 為 121- 。因此它不是一個(gè)回文數(shù)。

示例 3:

輸入: 10

輸出: false

解釋: 從右向左讀, 為 01 。因此它不是一個(gè)回文數(shù)。

進(jìn)階:

你能不將整數(shù)轉(zhuǎn)為字符串來(lái)解決這個(gè)問(wèn)題嗎?',

'#include

#include

#include

#include

#include

using namespace std;

class Solution{

public:

bool isPalindrome(int x)

{

//將你的代碼寫在下面

return true;

}

};',

'#ifndef COMPILER_ONLINE

#include "header.cpp"

#endif

void Test1()

{

// 通過(guò)定義臨時(shí)對(duì)象,來(lái)完成方法的調(diào)用

bool ret = Solution().isPalindrome(121);

if(ret){

std::cout << "通過(guò)用例1, 測(cè)試121通過(guò) ... OK!" << std::endl;

}

else{

std::cout << "沒(méi)有通過(guò)用例1, 測(cè)試的值是: 121" << std::endl;

}

}

void Test2()

{

// 通過(guò)定義臨時(shí)對(duì)象,來(lái)完成方法的調(diào)用

bool ret = Solution().isPalindrome(-10);

if(!ret){

std::cout << "通過(guò)用例2, 測(cè)試-10通過(guò) ... OK!" << std::endl;

}

else{

std::cout << "沒(méi)有通過(guò)用例2, 測(cè)試的值是: -10" << std::endl;

}

}

int main()

{

Test1();

Test2();

return 0;

}',

1,

30000

);

select * from oj_table;

?

?這里我只錄入了一道題為了測(cè)試

4.4 下載并引入mysql庫(kù)文件

MySQL :: Download MySQL Community Server

要使用C/C++連接MySQL,需要使用MySQL官網(wǎng)提供的庫(kù)

??

?下載完畢后需要將其上傳到云服務(wù)器,這里將下載的庫(kù)文件存放在下面的目錄:

?

然后使用tar命令將壓縮包解壓到當(dāng)前目錄下:?

xz -d mysql-8.0.37-linux-glibc2.28-i686.tar.xz

tar xvf mysql-8.0.37-linux-glibc2.28-i686.tar

?

進(jìn)入解壓后的目錄當(dāng)中,可以看到有一個(gè)include子目錄和一個(gè)lib子目錄,其中,include目錄下存放的一堆頭文件。而lib64目錄下存放的就是動(dòng)靜態(tài)庫(kù)。?

??

?然后在我們的項(xiàng)目中建立軟連接

?

4.5 一個(gè)BUG

如果你當(dāng)時(shí)下載myql把mysql-devel也下載了,不需要進(jìn)行上面步驟 這種引入第三方庫(kù)的操作,可能會(huì)因?yàn)榘姹静患嫒?而導(dǎo)致出錯(cuò)skipping incompatible ./lib/libmysqlclient.so when searching for -lmysqlclient ?建議直接安裝: yum -y install mysql-devel

?4.5 重新設(shè)計(jì)oj_model

因?yàn)閛j_model模塊是管理數(shù)據(jù),提供接口的模塊,所以要把這個(gè)項(xiàng)目變成mysql就需要重新設(shè)計(jì)

#pragma once

// 文件版本

#include "../comm/util.hpp"

#include "../comm/log.hpp"

#include

#include

#include

#include "./include/mysql.h"

namespace ns_model

{

using namespace std;

using namespace ns_log;

using namespace ns_util;

struct Question{

string number;// 題目編號(hào),唯一

string title;// 題目標(biāo)題

string star;// 難度: 簡(jiǎn)單 中等 困難

int cpu_limit;// 題目的時(shí)間復(fù)雜度(S)

int mem_limit;// 題目的空間復(fù)雜度(KB)

string desc;// 題目描述

string header; // 題目預(yù)設(shè)給用戶在線編輯器的代碼

string tail;// 題目測(cè)試用例,需要和header拼接

};

const std::string oj_questions = "oj_table";

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

{

public:

Model(){

}

~Model(){

;

}

bool QueryMysql(const std::string &sql,vector*out){

// 這里的out是輸出型參數(shù)

// 創(chuàng)建mysql句柄

MYSQL *my = mysql_init(nullptr);

// 連接數(shù)據(jù)庫(kù)

if(nullptr == mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0)){

LOG(FATAL) << "連接數(shù)據(jù)庫(kù)失敗!" << "\n";

return false;

}

// 一定要設(shè)置該鏈接的編碼格式,要不然會(huì)出現(xiàn)亂碼的問(wèn)題

mysql_set_character_set(my,"utf8");

LOG(INFO) << "連接數(shù)據(jù)庫(kù)成功!" << "\n";

// 執(zhí)行sql語(yǔ)句

if(0 != mysql_query(my,sql.c_str())){

LOG(WARNING) << sql << " execute error! " << "\n";

return false;

}

// 提取結(jié)果

MYSQL_RES *res = mysql_store_result(my);// 本質(zhì)就是一個(gè)2級(jí)指針

// 分析結(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);

}

// 釋放控件

free(res);

// 關(guān)閉mysql連接

mysql_close(my);

return true;

}

// 獲取所有題目,這里的out是輸出型參數(shù)

bool GetAllQuestions(vector*out){

std::string sql = "select * from ";

sql += oj_questions;

return QueryMysql(sql,out);

}

// 獲取指定題目,這里的q是輸出型參數(shù)

bool GetOneQuestion(const string& number,Question* q){

bool res = false;

std::string sql = "select * from ";

sql += oj_questions;

sql += " where number=";

sql += number;

vector result;

if(QueryMysql(sql,&result)){

if(result.size() == 1){

*q = result[0];

res = true;

}

}

return res;

}

private:

// 題號(hào) : 題目細(xì)節(jié)

unordered_map questions;

};

}

mysql_init: 創(chuàng)建mysql句柄mysql_real_connect: 創(chuàng)建mysql連接 mysql_query: 發(fā)起mysql請(qǐng)求 mysql_close: 關(guān)閉mysql連接

?4.6 相關(guān)測(cè)試

?

編譯期間告訴編譯器頭文件和庫(kù)文件在哪里?-I指明搜索的頭文件,-L指明搜索的lib并加上-lmysqlclient

??

??

?

5. 擴(kuò)展

功能上更完善一下,判斷一道題目正確之后,自動(dòng)下一道題目

基于注冊(cè)和登陸的錄題功能

.....

6. 完整項(xiàng)目鏈接?

projects/負(fù)載均衡/OnlineJudge at main · 1LYC/projects · GitHub

柚子快報(bào)激活碼778899分享:運(yùn)維 實(shí)戰(zhàn)項(xiàng)目: 負(fù)載均衡

http://yzkb.51969.com/

精彩內(nèi)容

評(píng)論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19028948.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問(wèn)

文章目錄