【负载均衡式在线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();

                    // 继续循环,继续请求
                }
            }
相关推荐
业余幻想家14 分钟前
Windows10/Windows11家庭版系统关闭自动更新
运维·windows
努力进修28 分钟前
跨地域传文件太麻烦?Nginx+cpolar 让本地服务直接公网访问
运维·nginx·cpolar
Qayrup37 分钟前
docker 搭建私有仓库,推送并拉取
运维·docker·容器
闪耀星星42 分钟前
debian elctron-builder
运维·debian
会飞的土拨鼠呀42 分钟前
Debian 12 笔记本合盖不休眠设置指南
运维·debian
梁正雄3 小时前
6、prometheus资源规划
运维·服务器·服务发现·prometheus·监控
晨曦之旅3 小时前
零成本体验云计算!阿贝云免费服务器深度测评
运维·服务器·云计算
工具人55553 小时前
Linux 抓取 RAM Dump 完整指南
linux·运维·安全
会飞的小蛮猪4 小时前
SkyWalking运维之路(Java探针接入)
java·运维·经验分享·容器·skywalking
天一生水water4 小时前
docker-compose安装
运维·docker·容器