【负载均衡oj】(七)ojserver

这篇具有很好参考价值的文章主要介绍了【负载均衡oj】(七)ojserver。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.目的

负责负载均衡,获取网站首页,通过网络罗调用编译并运行并提供结果给用户。根据用户的操作提供不同的功能。采用mvc结构。使用 ctemplate文字模板做数据渲染

m在这里指的是和数据的交互,可以用文件或者sql交互。在使用文件的情况下,就必须要有对应的文件目录存放每一道题。提供题目描述和题头还有测试用例。

二.实现model

负责将文件题库抽象成数据结构,并提供接口给ojcontrol调用。ojcontrol通过model模块获取全部的题目信息和测试用例,用来交给后端服务继续运行

题的数据结构

  typedef struct Question // 每一道题对应的基本信息
    {
        string number;    // 题目编号
        string title;     // 题目名称
        string diffculty; // 题目难度
        int cpu_limit;    // 运行时间限制
        int mem_limit;    // 内存时间限制
        string desc;      // 题目描述
        string header;    // 题目提供给用户的首部代码
        string tailer;    // 题目的测试用例,需要和header拼接,形成完整代码
    }Question;

获取题信息的数据结构,因为是从文件中读取,所以需要一个字符串格分割函数。

加载所有题库信息到数据结构。

 bool LoadQuestionList(const string question_path)   //根据文件题目列表获取到数据结构内
        {
            ifstream in(questionlist_path); // 打开题目列表的文件流

            if (!in.is_open()) // 打开文件流失败
            {
                LOG(FATAL) << "加载题库失败,请检查是否存在题库文件"
                           << "\n";
                return false;
            }

            string line;
            while (getline(in, line))
            {
                vector<string> tokens;                       // 题的五个信息
                StringUtil::SplitString(line, &tokens, " "); // 根据空格分隔出不同元素

                if (tokens.size() != 5) // 每道题有五种基本信息(编号,题目,,难度,mem,cpu)
                {
                    LOG(WARNING) << "加载部分题目失败, 请检查文件格式"
                                 << "\n";
                    continue; // 获取当前题目信息失败直接跳过
                }

                Question q; // 创建题目对象填充信息

                q.number = tokens[0];
                q.title = tokens[1];
                q.diffculty = tokens[2];
                q.cpu_limit = atoi(tokens[3].c_str());
                q.mem_limit = atoi(tokens[4].c_str());

                // 获取题目详细信息
                string path = question_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.tailer), true);

                // 形成哈希映射
                questions.insert({q.number, q});
            }
            LOG(INFO) << "题库加载成功"
                      << "\n";
            in.close();

            return true;
        }

获取一道题给客户

 bool GetOneQuestion(const string &number, Question *ret) // 通过题号获取对应题
        {
            const auto &iter = questions.find(number);
            if (iter == questions.end())
            {
                LOG(ERROR) << "用户获取题目失败, 题目编号: " << number << "\n";
                return false;
            }
            *ret = iter->second; // 输出行参数
            return true;
        }

获取所有题目给客户

bool GetAllQuestion(vector<Question> *out)
        {
            if (questions.size() == 0)
            {
                LOG(ERROR) << "用户获取题库失败"
                           << "\n";
                return false;
            }
            for (auto &q : questions)
            {
                out->push_back(q.second); // 遍历哈希映射把题目信息依次录入vector
            }
            return true;
        }

三.control模块

主要实现逻辑控制,从网页上拿来各种信息,提取有用信息后,结合本地数据向后端提交,如判题功能,或者根据要求获取对应本地信息,并通过前段模块返回给用户

 // 核心业务逻辑
    class Control // 控制ojserver的基础功能,包括修改题库和前端界面交互,整合数据控制和前端交互
    {
    private:
        ns_model::Model _model; // 提供后台数据
        View _view;             // 提供html渲染功能
        LoadBalance _load;      // 提供负载均衡模块
    public:
        void RecoveryMachine()
        {
            _load.OnlineMachine();
        }
        // 获取所有题目并生成html文件
        bool AllQuestions(string *html) // 输出型参数
        {
            vector<Question> all;
            if (_model.GetAllQuestion(&all)) // 获取所有题目到vector内
            {
                // 获取信息成功构建html
                sort(all.begin(),all.end(),[](const Question& q1,const Question& q2){ 
                    return atoi(q1.number.c_str())<atoi(q2.number.c_str());
                });
                _view.AllExpandHtml(all, html);
            }
            else
            {
                *html = "获取题目失败, 形成题目列表失败";
                return false;
            }
            return true;
        }

        // 获取一道题
        bool OneQuestion(const string &number, string *html)
        {
            Question q;

            if (_model.GetOneQuestion(number, &q))
            {
                _view.OneExepandHtml(q, html);
                return true;
            }
            else
            {
                *html = "指定题目: " + number + " 不存在!";
                return false;
            }
        }

        // 判断用户代码是否正确
        void Judge(const string &in_json, string *outj_son, const string &number)
        {
            // 需要读取上来用户的代码并和测试用例拼接成一份完整代码最后
            // 0. 根据题目编号,直接拿到对应的题目细节
            Question q;
            _model.GetOneQuestion(number, &q);

            // 1. in_json进行反序列化,得到题目的id,得到用户提交源代码,input
            Json::Reader reader;
            Json::Value usr_val;
            reader.parse(in_json, usr_val);
            string code = usr_val["code"].asString(); // 用户写的代码

            // 2. 重新拼接用户代码+测试用例代码,形成新的代码
            Json::Value compile_val;
            compile_val["code"] = code + '\n' + q.tailer;
            compile_val["input"] = usr_val["input"].asString();
            compile_val["cpu_limit"] = q.cpu_limit;
            compile_val["mem_limit"] = q.mem_limit;

            // 序列化准备传输
            Json::FastWriter writer;
            std::string compile_string = writer.write(compile_val);
            // 3. 选择负载最低的主机(差错处理)
            // 规则: 一直选择,直到主机可用,否则,就是全部挂掉
            while (true)
            {
                int id;
                Machine *m;
                // 跟据负载选择服务器
                if (!_load.SmartChoice(&m, &id))
                {
                    break; // 选择主机失败
                }
                // 4. 选择完主机后发起http请求链接到主机
                Client cli(m->_ip, m->_port);
                m->IncLoad();
                LOG(INFO) << " 选择主机成功, 主机id: " << id << " 详情: " << m->_ip << ":" << m->_port << " 当前主机的负载是: " << m->Load() << "\n";

                // 5.将数据提交给服务主机并进行编译运行
                if (auto res = cli.Post("/Compile_and_run", compile_string, "application/json;charset=utf-8")) // 这个请求完成编译运行并返回结果
                {
                    // 需要判断返回结果,有返回结果不一定成功运行
                    if (res->status == 200) // 200表示成功运行
                    {
                        // 将结果返回给用户
                        *outj_son = res->body;
                        m->DecLoad();
                        LOG(INFO) << "主机执行任务成功"
                                  << "\n";
                        break;
                    }
                    // 若提交失败也需要将负载复原并重新执行该流程
                    m->DecLoad();
                }
                else
                {
                    // 请求失败(当前主机不存在)
                    LOG(ERROR) << " 当前请求的主机id: " << id << " 详情: " << m->_ip << ":" << m->_port << " 可能已经离线"
                               << "\n";
                    _load.OfflineMachine(id);

                    _load.ShowMachines(); // 仅仅是为了用来调试
                }
            }
        }

四.view模块

在拿到model的题目信息后,结合本地html进行渲染,给用户提供前端展示页面。采用ctemplate渲染。

通过互取到的所有题目信息,使用ctemplate渲染展示给用户。

// 获取所有题目并生成html文件
        bool AllQuestions(string *html) // 输出型参数
        {
            vector<Question> all;
            if (_model.GetAllQuestion(&all)) // 获取所有题目到vector内
            {
                // 获取信息成功构建html
                sort(all.begin(),all.end(),[](const Question& q1,const Question& q2){ 
                    return atoi(q1.number.c_str())<atoi(q2.number.c_str());
                });
                _view.AllExpandHtml(all, html);
            }
            else
            {
                *html = "获取题目失败, 形成题目列表失败";
                return false;
            }
            return true;
        }

一道题

 bool OneQuestion(const string &number, string *html)
        {
            Question q;

            if (_model.GetOneQuestion(number, &q))
            {
                _view.OneExepandHtml(q, html);
                return true;
            }
            else
            {
                *html = "指定题目: " + number + " 不存在!";
                return false;
            }
        }

五.负载均衡

我们需要以一个文件存放所有可用的主机和端口号。作为主机的配置文件。同时主机需要保存自身的负载情况,所以需要加锁。

class Machine // 标识提供服务的主机
    {
    public:
        std::string _ip; // 本机的ip端口号和负载程度
        int _port;
        uint64_t _load;
        std::mutex *_mtx; // 重点必须用指针不能用实例,容器内有拷贝,c++里的mutex是禁止拷贝的,只能用指针取地址绕过

        Machine()
            : _ip(""),
              _port(0),
              _load(0),
              _mtx(nullptr)
        {
        }

        // 提升负载(有可能多个主机同时运行有竞争问题)
        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;
        }

        void ResetLoad()
        {
            if (_mtx)
            {
                _mtx->lock();
            }
            _load = 0;
            if (_mtx)
            {
                _mtx->unlock();
            }
        }
    };

负载均衡模块负责所有主机的情况,包括是否上线。并提供负载最小的主机,也需要加锁,因为涉及到对所有主机属性的更改。算临界区文章来源地址https://www.toymoban.com/news/detail-821011.html

class LoadBalance // 负载均衡模块
    {
        // 可以给我们提供编译服务的所有的主机
        // 每一台主机都有自己的下标,充当当前主机的id
        vector<Machine> machines;
        // 所有在线的主机id
        std::vector<int> online;
        // 所有离线的主机id
        std::vector<int> offline;
        std::mutex mtx; //需要保证负载均衡的数据安全

    public:
        LoadBalance()
        {
            assert(LoadConf(service_machine));
            LOG(INFO) << "加载 " << service_machine << " 成功"
                      << "\n";
        }

        bool LoadConf(const string &machine_conf) // 从conf文件里读取上来所有主机信息
        {
            ifstream in(machine_conf); // 打开文件流
            if (!in.is_open())
            {
                LOG(FATAL) << " 加载: " << machine_conf << " 失败"
                           << "\n";
                return false;
            }
            // 读取数据到line
            string line;
            while (getline(in, line))
            {
                vector<string> tokens; // 每一行切分到这里,只有两个元素,ip和端口号
                StringUtil::SplitString(line, &tokens, ":");

                if (tokens.size() != 2)
                {
                    LOG(WARNING) << " 切分 " << line << " 失败"
                                 << "\n";
                    continue;
                }
                // 读取数据完毕构建对象
                Machine m;
                m._ip = tokens[0]; // 填充ip和端口号
                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;
        }

        bool SmartChoice(Machine **m, int *id) // 两个输出出行参数,返回选择的主机,或者看情况离线主机
        {                                      // 因为传参的时候不想通过下标访问,所以通过地址修改
            // 1. 选择主机(更新该主机的负载)
            // 2. 我们需要可能离线该主机

            // 选择主机有安全问题
            mtx.lock();
            int online_num = online.size(); // 检查活跃主机数
            if (online_num == 0)
            {
                mtx.unlock();
                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++) // 寻找最小负载
            {
                uint64_t cur_load = machines[online[i]].Load();
                if (cur_load < min_load)
                {
                    min_load = cur_load;
                    *id = online[i];
                    *m = &machines[online[i]];
                }
            }
            mtx.unlock();
            return true;
        }

        void OnlineMachine() // 上线服务器(一次直接全部上线)
        {
            mtx.lock();
            online.insert(online.end(), offline.begin(), offline.end());
            offline.erase(offline.begin(), offline.end());
            LOG(INFO) << "重新登陆主机成功"
                      << "\n";
            mtx.unlock();
        }

        void ShowMachines() // 查看所有服务器状态
        {
            mtx.lock();
            std::cout << "当前在线主机列表: "
                      << "\n";
            for (auto &id : online)
            {
                std::cout << id << " ";
            }
            std::cout << std::endl;

            std::cout << "当前离线主机列表: "
                      << "\n";
            for (auto &id : offline)
            {
                std::cout << id << " ";
            }
            mtx.unlock();
        }

        void OfflineMachine(const int &mid) // 下线服务器
        {
            mtx.lock();
            for (auto iter = online.begin(); iter != online.end(); iter++) // 用迭代器遍历好一些,利于删除
            {
                if (*iter == mid)
                {
                    machines[mid].ResetLoad(); // 下线前清空负载
                    online.erase(iter);
                    offline.push_back(mid);

                    break; // 因为break所以不用考虑迭代器失效,此时循环已经终止了
                }
            }
            mtx.unlock();
        }
    };

到了这里,关于【负载均衡oj】(七)ojserver的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • nginx反向代理服务器及负载均衡服务配置

    一、正向代理与反向代理 正向代理:是一个位于客户端和原始服务器(oricin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。 正向代理的典型用途是为在防火

    2024年02月04日
    浏览(39)
  • 阿里云服务器F5负载均衡设置说明

    阿里云服务器F5负载均衡怎么设置?F5负载均衡和云服务器ECS绑定教程,先创建F5负载均衡器,然后创建后端服务器组,最后配置监听即可。可以设置IPv4和IPv6的负载均衡。阿里云服务器网aliyunfuwuqi.com整理 阿里云服务器F5负载均衡设置 教程,详细参考阿里云负载均衡页面:al

    2024年04月11日
    浏览(36)
  • F5服务器负载均衡能力如何?一文了解

    但凡知道服务器负载均衡这个名词的,基本都知道 F5,因为负载均衡是 F5 的代表作,换句话来说,负载均衡就是由 F5 发明的。提到F5服务器负载均衡能力如何?不得不关注F5提出的关于安全、网络全面优化的解决方案,在其分布式云的产品中,全局F5服务器负载均衡的优势得

    2024年02月09日
    浏览(29)
  • OpenSIPS 3.1 负载均衡 MRCP 服务器的实现

    FreeSWITCH 通过 unimrcp 模块来对接 MRCP 服务器,该模块在启动时会根据 mrcp profile 配置文件创建 MRCP 客户端。在使用 MRCP 功能时,FreeSWITCH 通过 mrcp profile 的名称来指定要使用的 MRCP 客户端,进而决定要连接的 MRCP 服务器。在 MRCPv2 服务器的负载分发方案分析 中笔者分析到只要做

    2024年02月06日
    浏览(41)
  • 游戏后端如何实现服务器之间的负载均衡?

    网络游戏已成为人们休闲娱乐的重要方式之一。而在游戏开发中,如何实现服务器之间的负载均衡是一个非常关键的问题。负载均衡不仅可以提高服务器的处理能力,还能保证游戏的稳定性和流畅性。本文将探讨游戏后端如何实现服务器之间的负载均衡。 一、负载均衡的概述

    2024年01月25日
    浏览(36)
  • Docker搭建Nginx+keepalived高可用负载均衡服务器

    一、背景 1.nginx高可用 在生产环境下,Nginx作为流量的入口,如果Nginx不能正常工作或服务器宕机,将导致整个微服务架构的不可用。所以负责负载均衡、反向代理的服务(Nginx)为了提高处理性能,高可用,也需要集群部署。本期咋们采用 keepalived 和 Nginx实现高可用。 2.Kee

    2024年04月22日
    浏览(39)
  • 企业架构LB-服务器的负载均衡之LVS实现

    学习目标和内容 1、能够了解LVS的基本工作方式 2、能够安装配置LVS实现负载均衡 3、能够了解LVS-NAT的配置方式 4、能够了解LVS-DR的配置方式 #一、LVS介绍和安装 == LVS ==(Linux Virtual Server)即Linux虚拟服务器,是由章文嵩博士主导的开源负载均衡项目,目前LVS已经被集成到Linux内

    2024年02月21日
    浏览(35)
  • 【网络面试(8)】防火墙原理、正向代理、反向代理、缓存服务器、负载均衡和内容分发服务器

     接上一篇:【网络面试必问(7)】聊聊集线器、交换机和路由器  作为一个程序员,其实很少去了解http请求消息在到达服务器之前,所经过的众多组件的,今天借着机会聊一聊标题中提到的正向代理、反向代理、缓存服务器、负载均衡和内容分发服务器等内容。 1.1 部署

    2024年02月22日
    浏览(34)
  • 【云服务器 ECS 实战】负载均衡 SLB 概述及配置选型

    1. 负载均衡概述 负载均衡就是 将访问流量根据转发策略分发到后端的多台服务器中 。负载均衡建立在现有网络结构之上,提供一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。简单理解就是将负载(

    2024年01月22日
    浏览(31)
  • nginx连接前后端分离项目 或 负载均衡映射多个服务器

    nginx的两种用法: 打通前后端项目,前后端分离的项目,通过nginx建立连接 负载均衡,一台机器请求转发至多个服务器 前端项目中的配置: 后端项目的ip和端口号就是正常的 下面看看nginx的配置文件: 将打包后的前端项目放这里: vue项目打包后会生成一个dist文件夹 放在服

    2024年02月05日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包