1.所用技术与开发环境

所用技术

:

C++ STL

标准库

Boost

准标准库

(

字符串切割

)

cpp

-

httplib

第三方开源网络库

ctemplate

第三方开源前端网页渲染库

jsoncpp

第三方开源序列化、反序列化库

负载均衡设计

多进程、多线程

MySQL C connect

Ace

前端在线编辑器

html/css/js/jquery/ajax 

2.开发环境

Centos 7

云服务器

vscode

Mysql Workbench

3. 项目宏观结构

我们的项目核心是三个模块

1.

comm

:

公共模块

2.

compile_server

:

编译与运行模块

3.

oj_server

:

获取题目列表,查看题目编写题目界面,负载均衡,其他功能

I. leetcode 结构

只实现类似

leetcode

的题目列表

+

在线编程功能

II. 我们的项目宏观结构

III. 编写思路

1.

先编写

compile_server

2.

oj_server

3.

version1

基于文件版的在线

OJ

4.

前端的页面设计

5.

version2

基于

MySQL

版的在线

OJ

4. compiler 服务设计

提供的服务:编译并运行代码,得到格式化的相关的结果

 第一个功能 compiler :编译功能

#pragma once

#include

#include

#include

#include

#include

#include

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

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

//只负责代码的编译

namespace ns_compiler

{

//引入路径拼接功能

using namespace ns_util;

using namespace ns_log;

class Compiler

{

public:

Compiler() = default;

~Compiler() = default;

//返回值:编译成功:True 编译失败:False

//输入参数:编译文件名

// file_name: 123

// 123 ->./temp/123.cpp

// 123 ->./temp/123.exe

// 123 ->./temp/123.stderr

static 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::Error(file_name).c_str(), O_CREAT | O_WRONLY, 0644);

if (_stderr < 0)

{

LOG(WARNING)<<"没有形成stderr文件"<<"\n";

exit(1);

}

dup2(_stderr, 2); //将错误信息重定向到文件中

//子进程使用程序替换完成代码的编译功能

execlp("g++","g++", "-o", PathUtil::Exe(file_name).c_str(),PathUtil::Src(file_name).c_str(), "-std=c++11", nullptr /*不要忘记*/);

LOG(ERROR) << "启动编译器g++失败,可能是参数传入有误"<<"\n";

exit(2);

}

else

{

waitpid(pid, nullptr, 0);

//编译是否成功,就看有没有形成同名的可执行文件

if (FileUtil::IsFileExists(PathUtil::Exe(file_name)))

{

LOG(INFO)<

return true;

}

}

LOG(ERROR)<<"编译失败,没有形成可执行文件"<<"\n";

return false;

}

};

}

Log

功能

#pragma once

#include

#include

#include "util.hpp"

namespace ns_log

{

//日志等级

enum

{

INFO,

DEBUG,

WARNING,

ERROR,

FATAL

};

//LOG()<<"message"

inline std::ostream &Log(const std::string &level,const std::string &file_name,const int line)

{

//添加日志等级

std::string message = "[";

message += level;

message += "]";

//添加报错文件名称

message += "[";

message += file_name;

message += "]";

//添加报错行

message += "[";

message += std::to_string(line);

message += "]";

//日志时间戳

message += "[";

message += ns_util::TimeUtil::GetTimeStamp();

message += "]";

std::cout << message;

return std::cout;

}

//开放日志

#define LOG(level) Log(#level,__FILE__,__LINE__)

}

第二个功能

runner

:运行功能

#pragma once

#include

#include

#include

#include

#include

#include

#include

#include

#include

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

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

namespace ns_runner

{

class Runner

{

public:

Runner() = default;

~Runner() = default;

public:

static void SetProcLimit(int cpu_limit,int mem_limit)

{

//设置CPU时长

struct rlimit _cpu_limit;

_cpu_limit.rlim_max = RLIM_INFINITY;

_cpu_limit.rlim_cur = cpu_limit;

setrlimit(RLIMIT_CPU,&_cpu_limit);

//设置内存大小

struct rlimit _mem_limit;

_mem_limit.rlim_max = RLIM_INFINITY;

_mem_limit.rlim_cur = mem_limit*1024; //转化成KB

setrlimit(RLIMIT_AS,&_mem_limit);

}

//返回值 > 0 ,程序异常,收到信号,返回值就是信号编号

//返回值 == 0 ,正常运行完毕,结果保存到对应的临时文件

//返回值 < 0 ,内部错误

//cpu_limit:该程序运行的时候,可以使用的最大CPU资源上限

//mem_limit:该程序运行的时候,可以使用最大内存

static int Run(const std::string &file_name,int cpu_limit,int mem_limit)

{

//只考虑是否正确运行,不考虑结果是否正确

/*

一个程序在默认启动的时候

标准输入:不处理

标准输出:结果

标准错误:运行时错误信息

*/

std::string _exectue = ns_util::PathUtil::Exe(file_name);

std::string _stdin = ns_util::PathUtil::Stdin(file_name);

std::string _stdout = ns_util::PathUtil::Stdout(file_name);

std::string _stderr = ns_util::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)

{

ns_log::LOG(ERROR)<<"运行时打开文件失败"<<"\n";

return -1; //文件打开失败

}

pid_t pid = fork();

if (pid < 0)

{

ns_log::LOG(ERROR)<<"运行时创建子进程失败"<<"\n";

close(_stdin_fd);

close(_stdout_fd);

close(_stderr_fd);

}

else if (pid == 0)

{

dup2(_stdin_fd,0);

dup2(_stdout_fd,1);

dup2(_stderr_fd,2);

SetProcLimit(cpu_limit,mem_limit);

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

exit(1);

}

else

{

// std::cout<<"关闭文件描述符"<

close(_stdin_fd);

close(_stdout_fd);

close(_stderr_fd);

int status = 0;

//进程异常收到信号

waitpid(pid, &status,0);

ns_log::LOG(INFO)<<"运行完毕,info:"<< (status & 0x7F) << "\n";

return status & 0x7F;

}

return 0;

}

};

}

测试资源限制:

#include

#include

#include

#include

#include

void handler(int signo)

{

std::cout << "signo : " << signo << std::endl; exit(1);

}

int main()

{

//资源不足,导致OS终止进程,是通过信号终止的 for(int i =1; i <= 31; i++)

{ signal(i, handler);

// struct rlimit r;

// r.rlim_cur = 1;

// r.rlim_max = RLIM_INFINITY;

// setrlimit(RLIMIT_CPU, &r);

//while(1); struct rlimit r;

r.rlim_cur = 1024 * 1024 * 40;

//20M r.rlim_max = RLIM_INFINITY;

setrlimit(RLIMIT_AS, &r);

int count = 0; while(true)

{

int *p = new int[1024*1024];

count++; std::cout << "size: " << count << std::endl;

sleep(1);

}

return 0;

}

}// 限

//

内存申请失败

terminate called after throwing an instance of

'std::bad_alloc'

what

():

std::bad_alloc

signo

:

6

[

whb@bite

-

alicloud OnlineJudge

]

$ kill

-

l

1

)

SIGHUP

2

)

SIGINT

3

)

SIGQUIT

4

)

SIGILL

5

)

SIGTRAP

6

)

SIGABRT

7

)

SIGBUS

8

)

SIGFPE

9

)

SIGKILL

10

)

SIGUSR1

11

)

SIGSEGV

12

)

SIGUSR2

13

)

SIGPIPE

14

)

SIGALRM

15

)

SIGTERM

16

)

SIGSTKFLT

17

)

SIGCHLD

18

)

SIGCONT

19

)

SIGSTOP

20

)

SIGTSTP

        

//CPU

使用超时

[

whb@bite

-

alicloud OnlineJudge

]

$

.

/

a

.

out

signo

:

24

[

whb@bite

-

alicloud OnlineJudge

]

$ kill

-

l

1

)

SIGHUP

2

)

SIGINT

3

)

SIGQUIT

4

)

SIGILL

5

)

SIGTRAP

6

)

SIGABRT

7

)

SIGBUS

8

)

SIGFPE

9

)

SIGKILL

10

)

SIGUSR1

11

)

SIGSEGV

12

)

SIGUSR2

13

)

SIGPIPE

14

)

SIGALRM

15

)

SIGTERM

16

)

SIGSTKFLT

17

)

SIGCHLD

18

)

SIGCONT

19

)

SIGSTOP

20

)

SIGTSTP

21

)

SIGTTIN

22

)

SIGTTOU

23

)

SIGURG

24

)

SIGXCPU

25

)

SIGXFSZ

26

)

SIGVTALRM

27

)

SIGPROF

28

)

SIGWINCH

29

)

SIGIO

30

)

SIGPWR

第三个功能

compile_run

:编译并运行功能

#pragma once

#include

#include

#include

#include

#include "compiler.hpp"

#include "complie_run.hpp"

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

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

#include "runner.hpp"

namespace ns_compile_and_run

{

class ComplieAndRun

{

public:

static std::string CodeToDesc(int code, const std::string &file_name)

{

std::string desc;

switch (code)

{

case 0:

desc = "编译运行成功";

break;

case -1:

desc = "用户提交的代码为空";

break;

case -2:

desc = "未知错误";

break;

case -3:

// desc = "代码编译是出现错误";

ns_util::FileUtil::ReadFile(ns_util::PathUtil::Error(file_name), &desc, true);

break;

case SIGABRT: // 6

desc = "内存超过范围";

break;

case SIGXCPU: // 24

desc = "CPU使用超时";

break;

case SIGFPE: // 8

desc = "浮点数溢出"; //除0

break;

default:

desc = "未知错误" + std::to_string(code);

break;

}

return desc;

}

public:

static void RemoveTempFile(const std::string &file_name)

{

std::vector AllTempFile{ns_util::PathUtil::Src(file_name),

ns_util::PathUtil::Error(file_name),

ns_util::PathUtil::Exe(file_name),

ns_util::PathUtil::Stderr(file_name),

ns_util::PathUtil::Stdin(file_name),

ns_util::PathUtil::Stdout(file_name)};

for(const auto &e :AllTempFile)

{

if(ns_util::FileUtil::IsFileExists(e));

unlink(e.c_str());

}

}

/*

输入:

code:用户给自己提交的代码

input:用户给自己的代码对应的输入,不作处理(后期可以扩展)

cpu_limit:时间复杂度

mem_limit:时间复杂度

输出:

status:状态码(必填)

reason:请求结果(必填)

stdout:程序运行结果(选填)

stderr:程序运行完的错误结果(选填)

*/

//参数

// in_json:{"code":"";"input":"";"cpu_limit":"";"mem_limit":"";}

// out_json:{"status":"0";"reason":"";"stdout":"";"stderr":""}

static void Start(const std::string &in_json, std::string *out_json)

{

Json::Value in_vaule;

Json::Reader reader;

reader.parse(in_json, in_vaule); //最后在差错处理

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

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

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

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

Json::Value out_vaule;

int status_code = 0;

std::string file_name;

int run_result = 0;

if (code.size() == 0)

{

// //最后差错处理

// out_vaule["status"] = -1; //代码为空

// out_vaule["reason"] = "用户提交的代码是空的";

// //序列化

// return;

status_code = -1;

goto END;

}

//形成唯一文件名 毫秒级时间戳 + 原子性递增唯一值

file_name = ns_util::FileUtil::UniqFileName();

if (!ns_util::FileUtil::WiterFile(ns_util::PathUtil::Src(file_name), code)) //形成临时源src文件

{

// out_vaule["status"] = -2; //未知错误

// out_vaule["reason"] = "提交的代码发生了未知错误";

// //序列化

// return;

status_code = -2;

goto END;

}

if (!ns_compiler::Compiler::Compile(file_name)) //编译失败

{

// out_vaule["status"] = -3;

// //编译失败的内容保存到了.error文件中,读取序列化

// out_vaule["reason"] = us_util::FileUtil::ReadFile(us_util::PathUtil::Error(file_name));

// //序列化

// return;

status_code = -3;

goto END;

}

run_result = ns_runner::Runner::Run(file_name, cpu_limit, mem_limit); //需要知道时间复杂度和空间复杂度

if (run_result < 0)

{

// out_vaule["status"] = -2; //未知错误

// out_vaule["reason"] = "发生了未知错误";

// //序列化

// return;

status_code = -2;

goto END;

}

else if (run_result > 0)

{

// out_vaule["status"] = -4; //运行时报错,收到信号

// out_vaule["reason"] = SignoToDesc(); //将信号转化成报错原因;

// //序列化

// return;

status_code = run_result;

goto END;

}

else

{

// //运行成功

// out_vaule["status"] = 0;

// out_vaule["reason"] = "运行成功";

status_code = 0;

}

END:

out_vaule["status"] = status_code;

out_vaule["reason"] = CodeToDesc(status_code,file_name);

if (status_code == 0)

{

//全部成功

std::string _stdout;

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

out_vaule["stdout"] = _stdout;

// std::cout<<"标准输出:"<<_stdout<

std::string _stderr;

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

out_vaule["stderr"] = _stderr;

}

//序列化

Json::StyledWriter writer;

*out_json = writer.write(out_vaule);

RemoveTempFile(file_name);

}

};

}

5. 基于MVC 结构的oj 服务设计

本质:建立一个小型网站

1.

获取首页,用题目列表充当

2.

编辑区域页面

3.

提交判题功能

(

编译并运行

)

M

:

Model

,

通常是和数据交互的模块,比如,对题库进行增删改查(文件版,

MySQL

V

:

view

,

通常是拿到数据之后,要进行构建网页,渲染网页内容,展示给用户的

(

浏览器

)

C

:

control

,

控制器,就是我们的核心业务逻辑

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

#include

#include "../comm/httplib.h"

#include "oj_control.hpp"

using namespace httplib;

int main()

{

//用户请求的的路由功能

Server svr;

ns_control::Control ctrl;

//获取所有题目列表

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

//返回一张带有所有题目的html网页

std::string html;

ctrl.ALlQuestions(&html);

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

});

//获取要根据题目编号,获取题目的内容

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

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

std::string html;

ctrl.Question(num,&html);

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

});

//用户提交代码,使用我们的判题功能()

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

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

std::string result_json;

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

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

// resq.set_content("指明题目的判题:"+num,"text/plain;charset=utf-8");

});

svr.set_base_dir("./wwwroot");

svr.listen("0.0.0.0",8080);

return 0;

}

第二个功能:model功能,提供对数据的操作

#pragma once

//文件版本

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

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

#include

#include

#include

#include

#include

#include

#include

// 根据题目list文件,加载所有的题目信息到内存中

// model: 主要用来和数据进行交互,对外提供访问数据的接口

namespace ns_model

{

using namespace std;

using namespace ns_log;

using namespace ns_util;

struct Question

{

std::string number; //题目编号,唯一

std::string title; //题目的标题

std::string star; //难度: 简单 中等 困难

int cpu_limit; //题目的时间要求(S)

int mem_limit; //题目的空间要去(KB)

std::string desc; //题目的描述

std::string header; //题目预设给用户在线编辑器的代码

std::string tail; //题目的测试用例,需要和header拼接,形成完整代码

};

const std::string questins_list = "./questions/questions.list";

const std::string questins_path = "./questions/";

class Model

{

private:

//题号 : 题目细节

unordered_map questions;

public:

Model()

{

assert(LoadQuestionList(questins_list));

}

bool LoadQuestionList(const string &question_list)

{

//加载配置文件: questions/questions.list + 题目编号文件

ifstream in(question_list);

if (!in.is_open())

{

LOG(FATAL) << " 加载题库失败,请检查是否存在题库文件"<< "\n";

return false;

}

string line;

while (getline(in, line))

{

vector tokens;

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

// 1 判断回文数 简单 1 30000

if (tokens.size() != 5)

{

LOG(WARNING) << "加载部分题目失败, 请检查文件格式" << "\n";

continue;

}

Question q;

q.number = tokens[0];

q.title = tokens[1];

//std::cout<

q.star = tokens[2];

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

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

string path = questins_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 GetAllQuestions(vector *out)

{

if (questions.size() == 0)

{

LOG(ERROR) << "用户获取题库失败"<< "\n";

return false;

}

for (const auto &q : questions)

{

out->push_back(q.second); // first: key, second: 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

第三个功能:control,逻辑控制模块

#pragma once

#include

#include

#include

#include

#include

#include

#include "oj_model.hpp"

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

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

#include "oj_view.hpp"

#include "../comm/httplib.h"

namespace ns_control

{

const std::string service_machine = "./conf/service_machine.conf";

//提供服务的主机

class Machine

{

public:

std::string ip; //编译服务的ip

int port; //编译服务的端口

uint64_t load; //编译服务的负载

std::mutex *mtx; //mutex禁止拷贝的,使用指针来完成

public:

Machine()

:ip("")

,port(0)

,load(0)

,mtx(nullptr)

{

}

~Machine()

{

}

public:

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;

}

};

//负载均衡模块

class LoadBlance

{

private:

std::vector machines; //可以提供编译服务所有主机

std::vector online; //所有在线的主机

std::vector offline; //所有离线主机

std::mutex mtx; //保证LoadBlance数据安全

public:

LoadBlance()

{

assert(LoadConf(service_machine));

ns_log::LOG(ns_log::INFO)<<" 加载 "<

}

~LoadBlance()

{

}

public:

bool LoadConf(const std::string &machine_conf)

{

std::ifstream in(machine_conf);

if(!in.is_open())

{

ns_log::LOG(ns_log::FATAL)<<"加载配置:"<

return false;

}

std::string line;

while(getline(in,line))

{

std::vector tokens;

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

if(tokens.size() != 2)

{

ns_log::LOG(ns_log::WARNING) <<" 切分 "<

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:输出型参数

//m :输出型参数

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

{

//1.使用选择好的主机(更新该主机的负载)

//2.我们需要可能离线该主机

mtx.lock();

//负载均衡的算法

//1.随机数 + hash

//2.轮询 + hash

int online_num = online.size();

if(online_num == 0)

{

mtx.unlock();

ns_log::LOG(ns_log::FATAL) << "后端编译服务全部挂掉了,请运维的老铁尽快查看"<<"\n";

return false;

}

//通过编译找到负载最小的机器

*id = online[0];

*m = &machines[online[0]];

uint64_t min_load = machines[online[0]].Load();

for(int i = 0; i < online_num; ++i)

{

min_load = min_load < machines[online[i]].Load() ? machines[online[i]].Load() : min_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)

{

online.erase(iter);

offline.push_back(which);

break;

}

}

mtx.unlock();

}

void OnlineMachine()

{

}

void ShowMachines()

{

mtx.lock();

std::cout<<"当前在线主机列表:";

for(auto &id : online)

{

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

}

std::cout<

for(auto &id : offline)

{

std::cout<<"当前离线主机列表:";

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

}

std::cout<

mtx.unlock();

}

};

class Control

{

private:

ns_model::Model _model; //提供后台服务

ns_view::View _view; //提供html渲染功能

LoadBlance _load_blance; //提供负载均衡器

public:

Control()

{

}

//根据题目数据构建网页

bool ALlQuestions(std::string *html)

{

std::vector all;

if(_model.GetAllQuestions(&all))

{

//获取题目信息成功,将所有的题目数据构建成网页

_view.AllExpandHtml(all,html);

}

else

{

*html = "获取题目失败,形成题目列表失败";

return false;

}

return true;

}

bool Question(const std::string number,std::string *html)

{

ns_model::Question q;

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

{

//获取指定题目成功,将题目数据构建成网页

_view.OneExpandHtml(q,html);

}

else

{

*html = "指定题目" + number + "不存在";

return false;

}

return true;

}

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

{

//0.根据题号,直接拿到题目细节

ns_model::Question q;

_model.GetOneQuestion(number,&q);

//1.in_json进行反序列化,得到题目的id,得到用户提交的源代码,input

Json::Reader reader;

Json::Value in_value;

reader.parse(in_json,in_value);

//2.重新拼接用户代码 + 测试用例代码,形成新代码

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

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 complie_string = writer.write(compile_value);

//3.选择负载最低的主机(差错处理)

for( ; ;)

{

int id = 0;

Machine *m = nullptr;

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

{

break;

}

ns_log::LOG(ns_log::INFO) <<"选择主机成功"<ip<<":"<port<<"\n";

//4.然后发起http请求,得到结果

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

m->IncLoad();

if(auto res = cli.Post("/compile_and_run",complie_string, "application/json;charset=utf-8"))

{

//5.将结果赋值给out_json

if(res->status == 200)

{

*out_json = res->body;

m->DecLoad();

break;

}

m->DecLoad();

}

else

{

//请求失败

ns_log::LOG(ns_log::ERROR)<<"详情:" << id <<":"<< m->ip << ":"<port<<"可能已经离线"<<"\n";

_load_blance.OfflineMachine(id);

_load_blance.ShowMachines();

}

}

}

~Control()

{

}

};

}

附加功能:需要有数据渲染

//

如果后续引入了

ctemplate

,一旦对网页结构进行修改,尽量的每次想看到结果,将

server

重启一下。

ctemplate

有 自己的优化加速策略,可能在内存中存在缓存网页数据(old)

 

当我们完成全部功能之后,需要注意:

要给编译模块添加—D条件编译掉测试用例中的头文件incldue

6. version1 文件版题目设计

1.

题目的编号

2.

题目的标题

3.

题目的难度

4.

题目的描述,题面

5.

时间要求

(

内部处理

)

6.

空间要求

(

内部处理

)

两批文件构成

1.

第一个:

questions

.

list

:

题目列表(不需要题目的内容)

2.

第二个:题目的描述,题目的预设置代码

(

header

.

cpp

),

测试用例代码

(

tail

.

cpp

)

这两个内容是通过题目的编号,产生关联的

1.

当用户提交自己的代码的时候:

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

void Test1()

{

// 通过定义临时对象,来完成方法的调用

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

if(ret){

std::cout << "通过用例1, 测试121通过 ... OK!" << std::endl;

}

else{

std::cout << "没有通过用例1, 测试的值是: 121" << std::endl;

}

}

void Test2()

{

// 通过定义临时对象,来完成方法的调用

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

if(!ret){

std::cout << "通过用例2, 测试-10通过 ... OK!" << std::endl;

}

else{

std::cout << "没有通过用例2, 测试的值是: -10" << std::endl;

}

}

int main()

{

Test1();

Test2();

return 0;

}

后端全部写完使用Postman 来测试

 

 

 

7. 前端页面设计

8. version2 MySQL版题目设计

9.综合测试

10.项目扩展思路

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: