【负载均衡式在线OJ】实现负载均衡

目录

管理服务器

[增加负载 && 减少负载](#增加负载 && 减少负载)

[重置负载 && 获得负载](#重置负载 && 获得负载)

负载均衡

添加配置信息

什么是负载均衡

如何实现?


管理服务器

增加负载 && 减少负载

客户端访问一次服务器,负载就加1。客户端结束访问服务器,负载就需要减1。

因为负载是临界资源,存在多台客户端访问同一台服务器的情况,有的客户端要增加服务器的负载,有的客户端要减少服务器的负载,如果不对负载这个临界资源做保护,就会存在数据不一致的情况。

所以需要加锁,保护临界资源。

cpp 复制代码
        // 增加负载
        void InLoad()
        {
            // 判断锁不是空指针
            if (_mtx)
                _mtx->lock();
            ++_load;
            if (_mtx)
                _mtx->unlock();
        }

         // 减少负载
        void DeLoad()
        {
            // 锁不是空指针
            if (_mtx)
                _mtx->lock();
            --_load;
            if (_mtx)
                _mtx->unlock();
        }

重置负载 && 获得负载

如果服务器下线了,就需要重置负载。

当该服务器再次上线时,才可以保证负载是正确的,不受过去数据的影响。

对于获取负载,为了在获取负载时保证数据一致,就需要加锁,如果在加锁后直接 return,就会导致锁没有释放。

为了既保证数据一致,又能释放锁,就需要定义局部变量 load。

cpp 复制代码
        // 重置负载
        void ResetLoad()
        {
            // 锁不是空指针
            if (_mtx)
                _mtx->lock();
            _load = 0;
            if (_mtx)
                _mtx->unlock();
        }

        // 获取该主机的负载
        uint64_t Load()
        {
            uint64_t load = 0;
            if (_mtx)
                _mtx->lock();
            load = _load;
            if (_mtx)
                _mtx->unlock();
            return load;
        }

负载均衡

添加配置信息

设置一个配置文件,文件中记录服务器的 IP 和 端口号,为了方便测试,IP 设为环回地址:

什么是负载均衡

通过在多个服务器之间分散工作负载,即使某个服务器出现故障,其他服务器也能继续处理请求,从而提高了系统的可靠性和可用性。负载均衡可以智能地将请求分发给当前负载较低的服务器,这有助于减少响应时间,并提高整体系统性能。

如何实现?

遍历在线列表中的服务器,得出负载最小的服务器,把用户提交的代码和测试用例合并之后,封装成报文发给负载最小的服务器。

  • 如果应答报文为空:在本项目中,服务器下线时不会对外发送退出信号,所以请求服务时,服务器可能已经下线了,如果应答报文为空,说明主机下线了,请求服务失败,此时需要重新选择服务器;
  • 如果应答报文不为空:
    • 如果应答报文的状态码为 200,说明服务器成功编译并运行了报文中的代码,此时不需要再选择服务器了;
    • 如果应答报文的状态码不为 200,说明请求服务失败了,就重新选择服务器。
cpp 复制代码
        // 判题
        // in_json:用户提交的代码的序列化字符串,out_json:输出型参数,返回判题结果
        void Judge(const std::string &number, const std::string in_json, std::string *out_json)
        {
            // 获取对应的题目信息
            struct Question q;
            _model.GetOneQuestion(number, &q);

            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);
            std::string code = in_value["code"].asString();

            // 序列化
            Json::Value compile_value;
            compile_value["code"] = code + "\n" + q.tail;
            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);

            // 循环式选择主机
            while (true)
            {
                // 选择主机
                Machine *m = nullptr;
                int id = 0;
                if (!_loadbalance.SmartChoice(&id, &m))
                {
                    // 没有主机在线
                    break;
                }

                // 得到主机
                Client cli(m->_ip, m->_port);

                // 增加负载
                m->InLoad();

                LOG(INFO) << " 选择主机成功,主机id:" << id << " 详情:" << m->_ip << ":" << m->_port << " 当前主机的负载是:" << m->Load() << "\n";

                // 发起请求
                /// compile_and_run:要请求的资源,
                auto res = cli.Post("/compile_and_run", compile_string, "application/json;charset=utf-8");
               
                if (res)
                {
                    // 请求成功
                    if (res->status == 200)// 判题成功
                    {
                        // 收到判题结果
                        *out_json = res->body;

                        // 减少负载
                        m->DeLoad();

                        LOG(INFO) << "请求编译合运行服务成功..." << "\n";

                        // 得到编译运行结果,判题成功,退出循环
                        break;
                    }

                    // 判题失败,状态码不是200
                    // 减少负载
                    m->DeLoad();

                    // 因为请求结果错误,故继续循环,继续选择主机,继续请求
                }
                else
                {
                    // 请求失败,返回值 res 为 nullptr,可能是主机下线导致的
                    LOG(ERROR) << " 当前请求的主机id:" << id << " 详情:" << m->_ip << ":" << m->_port << " 可能已经离线" << "\n";
                    
                    // 从在线列表中下线该主机
                    _loadbalance.OfflineMachine(id);

                    // 打印日志信息,用于测试
                    _loadbalance.ShowMachines();

                    // 继续循环,继续请求
                }
            }
相关推荐
艾莉丝努力练剑2 分钟前
【Linux网络】Linux 网络编程:HTTP(四)从手写服务器到生产级 Nginx 与 cpp-httplib 实战
linux·运维·服务器·网络·c++·nginx·http
@insist1238 分钟前
信息安全工程师-安全实施:等保 2.0 框架、核心机制与运维体系
运维·安全·软考·信息安全工程师·软件水平考试
Harm灬小海11 分钟前
【云计算学习之路】学习Centos7系统:Linux磁盘管理
linux·运维·服务器·学习·云计算
艾莉丝努力练剑16 分钟前
【Linux网络】Linux 网络编程:HTTP(三)HTTP 协议原理
linux·运维·服务器·网络·c++·http
古怪今人18 分钟前
WSL和Hyper-V Ubuntu安装docker Docker安装Reids、MySQL、PostgreSQL和RabbitMQ
运维·docker·容器
安妮的小熊呢22 分钟前
CRMEB标准版v6.0: 商城DIY装修新升级,PS级自由设计!
运维·javascript·平面·信息可视化·小程序·开源软件
米高梅狮子23 分钟前
01.ELK企业日志分析系统
运维·服务器·网络·数据库·elk·oracle
逆境不可逃23 分钟前
Hello-Agents 第二部分-第九章总结:上下文工程
linux·运维·服务器
阿达hi23 分钟前
RPA 自动化SAP 流程授权
运维·自动化·rpa
团象科技24 分钟前
跨境业务链路频繁卡壳时,海外云服务器如何优化成本结构
运维·服务器