Delete 'src/my-online-judge-master/MYOJ/OJ_server/OJ_Control.hpp'

master
pk4wt2yal 1 week ago
parent 4f230ed54c
commit 87e37f0ac2

@ -1,372 +0,0 @@
#pragma once
#include <atomic>
#include <mutex>
#include <assert.h>
#include <jsoncpp/json/json.h>
#include "OJ_view.hpp"
#include "../comm/Comm_model_MySQL.hpp"
#include "../comm/util.hpp"
#include "../comm/httplib.h"
namespace ns_control
{
const std::string http_pattern = "/compile_and_run";
class Machine
{
public:
Machine(const std::string &IP, const uint16_t port)
: _IP(IP), _port(port), a_loads(0)
{
}
~Machine()
{
}
void inLoad()
{
a_loads.fetch_add(1, std::memory_order_relaxed);
}
void deLoad()
{
a_loads.fetch_sub(1, std::memory_order_relaxed);
}
void resetLoad()
{
a_loads.store(0, std::memory_order_relaxed);
}
uint32_t getLoad() const
{
return a_loads.load(std::memory_order_relaxed);
}
std::string getIP() const
{
return _IP;
}
uint16_t getPort() const
{
return _port;
}
Machine(const Machine &) = delete;
Machine &operator=(const Machine &) = delete;
Machine(Machine &&other) noexcept
: _IP(std::move(other._IP)), _port(other._port), a_loads(other.a_loads.load())
{
}
// 移动赋值运算符
Machine &operator=(Machine &&other) noexcept
{
if (this != &other)
{
_IP = std::move(other._IP);
_port = other._port;
a_loads.store(other.a_loads.load());
other.a_loads.store(0);
}
return *this;
}
private:
std::string _IP; // 主机IP地址
uint16_t _port; // 主机端口号
std::atomic_uint a_loads; // 负载量,原子类型,保证线程安全
};
const std::string conf_path = "/home/zzy1/2024/my-online-judge/MYOJ/OJ_server/service_machine.conf";
class LoadBalance
{
public:
LoadBalance()
{
// 导入配置文件
if (!loadConf())
{
// 导入失败,差错问题以在loadConf中说明,此处直接退出
exit(1);
}
lg(Info, "加载配置文件%s成功!\n", conf_path.c_str());
}
~LoadBalance()
{
}
bool loadConf()
{
// 1.打开文件流
std::ifstream in(conf_path);
if (!in.is_open())
{
// 打开失败的情况
lg(Error, "加载配置文件%s失败, 请检查! errno: %d, strerror: %s\n", conf_path.c_str(), errno, strerror(errno));
return false;
}
std::string line; // 从文件流中读取缓冲区
std::vector<std::string> tokens; // 分割字符串后的结果缓冲区
while (getline(in, line))
{
ns_util::splitUtil::splitString(line, ":", &tokens);
if (tokens.size() != 2)
{
lg(Error, "分割字符串失败, errno: %d, strerror: %s\n", errno, strerror(errno));
return false;
}
Machine machine(tokens[0], stoi(tokens[1]));
// 先将配置文件中的所有主机按照上线处理
_onlines.push_back(_machines.size());
_machines.push_back(std::move(machine));
}
// 从文件流中读取完毕,回收资源
in.close();
return true;
}
// 两个输出型参数分别是主机的ID号和描述主机的二级指针以及返回是否选择成功
bool smartChoice(int *num, Machine **ppmachine)
{
// 1. 判断是否还有主机在线
_mutex.lock();
if (_onlines.size() == 0)
{
// 所有主机均已下线
lg(Fatal, "所有主机均已下线, 请运维人员检查主机状况!\n");
return false;
}
// 2.遍历所有在线的主机表,找到负载最小的主机号
int min_num = _onlines[0]; // 用来存储负载最小的主机的编号
uint32_t min_load = _machines[min_num].getLoad(); // 用来存储负载最小的主机的负载量
for (int i = 1; i < _onlines.size(); i++)
{
int tmp_num = _onlines[i];
uint32_t tmp_load = _machines[tmp_num].getLoad();
if (min_load > tmp_load)
{
min_load = tmp_load;
min_num = tmp_num;
}
}
_mutex.unlock();
*num = min_num;
*ppmachine = &_machines[min_num];
return true;
}
// 上线所有主机
void onlineMachine()
{
_mutex.lock();
_onlines.insert(_onlines.end(), _offlines.begin(), _offlines.end());
_offlines.clear();
_mutex.unlock();
}
// 展示所有主机分别将离线主机在线主机打印出来主要debug使用
void showMachine()
{
std::cout << "在线主机: ";
for (int x : _onlines)
{
std::cout << x << " ";
}
std::cout << std::endl;
std::cout << "离线主机: ";
for (int x : _offlines)
{
std::cout << x << " ";
}
std::cout << std::endl;
}
// 下线指定主机
void offlineMachine(int host_id)
{
_mutex.lock();
if (_machines.size() <= host_id)
{
lg(Warning, "要下线的主机: %d 不存在!\n", host_id);
return;
}
for (auto it = _onlines.begin(); it != _onlines.end(); it++)
{
if (*it == host_id)
{
// 由于直接break所以不需要考虑迭代器失效的问题
_onlines.erase(it);
_offlines.push_back(host_id);
break;
}
}
_mutex.unlock();
}
private:
std::vector<Machine> _machines; // 存储所有的主机,每个主机的下标就是天然的主机编号
std::vector<int> _onlines; // 记录所有上线状态的主机编号
std::vector<int> _offlines; // 记录所有下线状态的主机编号
std::mutex _mutex; // 锁由于STL不保证线程安全需要加锁保护
};
// 核心控制器
class Control
{
public:
Control()
{
}
~Control()
{
}
// 将所有主机恢复为在线状态
void recoveryMachine()
{
_lb.onlineMachine();
}
// 获取所有问题
bool allQuestions(std::string *html)
{
std::vector<ns_model_MySQL::Question> questions;
if (!_model.getAllQuestions(&questions))
{
lg(Error, "获取所有题目失败, 无法构成网页!\n");
*html = "获取所有题目失败, 无法构成网页!";
return false;
}
std::sort(questions.begin(), questions.end(),
[](const ns_model_MySQL::Question &q1, const ns_model_MySQL::Question &q2)
{
int num1 = std::stoi(q1.number);
int num2 = std::stoi(q2.number);
assert(!(num1 == num2));
return (num1 < num2);
});
_view.AllExpandHtml(questions, html);
return true;
}
bool oneQuestion(const std::string &num, std::string *html)
{
ns_model_MySQL::Question q;
if (!_model.getOneQuestion(num, &q))
{
lg(Error, "获取题号%s的题目信息失败, 无法构建网页!\n", num.c_str());
*html = "获取单个题目失败,无法构成网页!";
return false;
}
_view.OneExpandHtml(q, html);
return true;
}
void Judge(const std::string &num, const std::string &in_json, std::string *out_json)
{
// 1.根据题号,拿到相应的题目信息
ns_model_MySQL::Question q;
_model.getOneQuestion(num, &q);
// 2.对题目信息反序列化,保存到json串中,其中in_json中的主要信息有code和input
Json::Value in_value;
Json::Reader reader;
reader.parse(in_json, in_value);
// 3.构建compile_server所需要的json串
Json::Value compile_value;
std::string code = in_value["code"].asString();
code += "\n";
code += q.tail;
compile_value["code"] = code;
compile_value["input"] = in_value["input"].asString();
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);
// 4.选择负载均衡最低的主机
// 注意:并不是一寻找就能找到的,可能找到的主机已经挂掉了
// 这个时候就要将挂掉的主机放入offlines中
// 然后再去寻找新的主机
// 这样结果要么就直接找到,要么就全部挂掉
int host_id = 0;
while (true)
{
Machine *pmachine = nullptr;
if (!_lb.smartChoice(&host_id, &pmachine))
{
// 主机全挂了
break;
}
// 选到了一个主机
// 5. 向主机发送http请求,得到结果,通过返回的状态码判断主机是否还在线
httplib::Client cli(pmachine->getIP(), pmachine->getPort());
// 发送post请求并增加负载
pmachine->inLoad();
lg(Info, "选择主机id: %d, ip: %s, port: %d, load: %d 尝试进行编译运行服务...\n",
host_id, pmachine->getIP().c_str(), pmachine->getPort(), pmachine->getLoad());
// std::cout << "发起判题请求" << http_pattern.c_str() << std::endl;
httplib::Result res = cli.Post(http_pattern.c_str(), compile_string, "application/json;charset=utf-8");
// httplib::Result 类型使httplib库中的定义的类其中重载了->操作符
// 可以用于获取response中的各种信息
// 判断结果有效并且状态码为200才说明是成功的
if (res && res->status == 200)
{
*out_json = res->body;
// 请求完毕,减少负载
pmachine->deLoad();
lg(Info, "编译运行服务完成...\n");
break;
}
else
{
// 失败,需要重新请求,并且将此次选择的主机放到下线表中
lg(Warning, "本次请求的主机id: %d, 请求失败, ip: %s, port: %d\n",
host_id, pmachine->getIP().c_str(), pmachine->getPort());
pmachine->resetLoad();
_lb.offlineMachine(host_id);
_lb.showMachine(); // for debug
}
}
}
private:
ns_view::View _view;
ns_model_MySQL::Model _model;
LoadBalance _lb;
};
}
Loading…
Cancel
Save