项目:基于httplib/消息队列负载均衡式在线OJ

news2024/11/22 6:17:10

文章目录

  • 写在前面
    • 关于组件
    • 开源仓库和项目上线
    • 其他文档说明
    • 项目亮点
  • 使用技术和环境
  • 项目宏观结构
  • 模块实现
    • compiler模块
    • runner模块
    • compile_run模块
    • compile_server模块
  • 基于MVC结构的OJ服务
    • 什么是MVC?
    • 用户请求服务路由功能
    • Model模块
    • view模块
    • Control模块

写在前面

关于组件

本项目使用的是一个仿RabbitMQ消息队列组件,关于该组件的细节内容在下面的链接中:

项目:仿RabbitMQ实现的消息队列组件

开源仓库和项目上线

本项目已开源到下面链接下的仓库当中

负载均衡式在线OJ

并且项目已经部署在了Linux服务器上,具体访问方式可以点击下面链接进行访问:

111.230.81.226:8080

其他文档说明

针对于日志的信息,我采用了之前写的一份利用可变参数实现日志的代码,具体链接如下

C++:可变参数实现日志系统

项目:仿RabbitMQ实现的消息队列组件

项目亮点

从技术栈的角度来讲,用的比较多,这里实现了两个版本,一个是httplib版本,在将请求从服务端发送到编译模块的部分是采用了httplib来进行发送的,而第二个版本采用的是消息队列的方式,服务端作为的是生产者,把数据推送到指定的队列,而编译模块作为消费者,来把数据进行编译后写入到Redis中,这样服务端只需要从Redis中获取数据即可

使用技术和环境

本项目使用的技术主要有:

  1. C++STL
  2. Boost标准库
  3. cpp-httplib网络库
  4. JSON序列化反序列化库
  5. Redis
  6. 消息队列

开发环境:

  1. Ubuntu22.04云服务器
  2. vscode

项目宏观结构

对于项目来说,核心的部分其实就是三个模块:

  1. common:存储的是一些公共的功能
  2. compile_server:编译运行模块
  3. oj_server:获取题目列表,查看界面,负载均衡等功能

具体的逻辑图如下所示:

对于客户端来说,会把所有的请求都发布到oj_server上,而oj_server就会把这些请求进行合理的分配,分配到各个地方进行各自的处理,其中对于请求题目和编写这样的内容不算特别吃资源,因此直接正常进行使用即可,而对于进行判定这样的操作,对于CPU的占用比较大,因此就需要使用负载均衡这样的策略,分配到各自的服务器主机上进行判定,达到一个负载均衡的效果

在这里插入图片描述

模块实现

下面进行模块的介绍实现,先说服务端,服务端主要包含下面的几个模块:

在这里插入图片描述

compiler模块

对于在线OJ来说,编译模块是必不可少的,那对于编译的具体逻辑,简单来说就是远端把代码提交到后端,此时会生成一个临时文件,而对于临时文件来说,就会进行编译的操作,编译失败,就把失败的信息生成,如果编译成功就继续执行,具体流程可以总结如下:

在这里插入图片描述
具体实现如下所示:

#pragma once
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "../common/util.hpp"
#include "../common/Log.hpp"

// 只负责进行代码的编译
class Compiler
{
public:
    Compiler()
    {}
    ~Compiler()
    {}
    static bool Compile(const string &file_name)
    {
        pid_t pid = fork();
        if(pid < 0)
        {
            lg(Error, "内部错误,创建子进程失败");
            return false;
        }
        else if (pid == 0)
        {
            umask(0);
            int _stderr = open(PathUtil::CompilerError(file_name).c_str(), O_CREAT | O_WRONLY, 0644);
            if(_stderr < 0)
            {
                lg(Warning, "没有生成stderr文件");
                exit(1);
            }
            dup2(_stderr, 2);
            //g++ -o target src -std=c++11
            execlp("g++", "g++", "-o", PathUtil::Exe(file_name).c_str(),\
            PathUtil::Src(file_name).c_str(), "-D", "COMPILER_ONLINE","-std=c++11",  nullptr);
            lg(Error, "启动编译器失败");
            exit(2);
        }
        else
        {
            waitpid(pid, nullptr, 0);
            // 编译是否成功,就看有没有形成对应的可执行程序
            if(FileUtil::IsFileExists(PathUtil::Exe(file_name)))
            {
                lg(Info, "%s:编译成功", PathUtil::Src(file_name).c_str());
                return true;
            }
        }
        lg(Error, "编译失败");
        return false;
    }
};

runner模块

编译了之后,就要运行了,因此下面这个模块就是运行模块

进程占用资源

对于在线OJ来说,其实是有时间复杂度和空间复杂度的需求的,因此我们也需要设置对应的接口,想要对于资源进行限制,需要用到的系统调用是setrlimit

在这里插入图片描述
对于这个接口来说,比较重要的参数就是这个结构体了:

在这里插入图片描述
这个结构体就是做出了对于软件和硬件的限制,总的来说这个系统调用还是比较简单的,我们需要限定CPU的时长和内存大小,需要用到的参数是RLIMIT_CPURLIMIT_AS

//提供设置进程占用资源大小的接口
static void SetProcLimit(int _cpu_limit, int _mem_limit)
{
    // 设置CPU时长
    struct rlimit cpu_rlimit;
    cpu_rlimit.rlim_max = RLIM_INFINITY;
    cpu_rlimit.rlim_cur = _cpu_limit;
    setrlimit(RLIMIT_CPU, &cpu_rlimit);

    // 设置内存大小
    struct rlimit mem_rlimit;
    mem_rlimit.rlim_max = RLIM_INFINITY;
    mem_rlimit.rlim_cur = _mem_limit * 1024; //转化成为KB
    setrlimit(RLIMIT_AS, &mem_rlimit);
}

程序运行

接下来就是让程序进行运行了,程序运行主要有三种情况:

  1. 代码跑完,结果正确
  2. 代码跑完,结果不正确
  3. 代码没跑完,异常了

那在运行模块,我们只管新的是程序有没有正常跑完,只要跑完了就可以,至于结果正确还是不正确,并不是这里关心的重点,因此在这个模块中,其实也需要有标准输出和标准错误两个模块,这两个模块当中记录的是到底有没有正常结束的信息

static int Run(const string &file_name, int cpu_limit, int mem_limit)
{
    string _execute = PathUtil::Exe(file_name);
    string _stdin   = PathUtil::Stdin(file_name);
    string _stdout  = PathUtil::Stdout(file_name);
    string _stderr  = 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)
    {
        lg(Error, "打开标准文件失败");
        return -1; //代表打开文件失败
    }            

    pid_t pid = fork();
    if (pid < 0)
    {
        lg(Error, "创建子进程失败");
        close(_stdin_fd);
        close(_stdout_fd);
        close(_stderr_fd);
        return -2; //代表创建子进程失败
    }
    else if (pid == 0)
    {
        dup2(_stdin_fd, 0);
        dup2(_stdout_fd, 1);
        dup2(_stderr_fd, 2);
        SetProcLimit(cpu_limit, mem_limit);
        execl(_execute.c_str()/*执行谁*/, _execute.c_str()/*如何执行该程序*/, nullptr);
        exit(1);
    }
    else
    {
        close(_stdin_fd);
        close(_stdout_fd);
        close(_stderr_fd);
        int status = 0;
        waitpid(pid, &status, 0);
        // 输出信号信息
        lg(Info, "运行结束,info:%d", (status & 0x7F));
        return status & 0x7F;
    }
}

compile_run模块

下面是进行compile_run模块,这个模块就是对于之前内容的整合,同时借助JSON完成序列化和反序列化的内容

对于序列化和反序列化来说,要传递的参数其实比较简单:

输入

  1. code:用户提交的代码
  2. input:用户给代码做的输入
  3. cpu_limit:时间要求
  4. mem_limit:空间要求

输出

  1. status:状态码
  2. reason:请求结果
  3. stdout:运行结果
  4. stderr:错误结果

所以在JSON中,它的组织形式更像是:

in_json: {"code": "#include...", "input": "","cpu_limit":1, "mem_limit":10240}
out_json: {"status":"0", "reason":"","stdout":"","stderr":"",}

资源清理

在本项目中的策略是把创建的临时文件都放到一个temp文件夹路径下,而当运行结果结束之后,也就没有存在的必要了,因此就需要显示的把这些临时文件都清理掉,所以是需要一个垃圾回收的机制存在的

代码实现如下:

#pragma once
#include "compiler.hpp"
#include "runner.hpp"
#include "../common/util.hpp"
#include <signal.h>
#include <unistd.h>
#include <jsoncpp/json/json.h>
using namespace std;

class CompileAndRun
{
public:
    static void RemoveTempFile(const string &file_name)
    {
        // 清理文件的个数是不确定的,但是有哪些我们是知道的
        string _src = PathUtil::Src(file_name);
        if(FileUtil::IsFileExists(_src)) 
            unlink(_src.c_str());

        string _compiler_error = PathUtil::CompilerError(file_name);
        if(FileUtil::IsFileExists(_compiler_error)) 
            unlink(_compiler_error.c_str());

        string _execute = PathUtil::Exe(file_name);
        if(FileUtil::IsFileExists(_execute)) 
            unlink(_execute.c_str());

        string _stdin = PathUtil::Stdin(file_name);
        if(FileUtil::IsFileExists(_stdin)) 
            unlink(_stdin.c_str());

        string _stdout = PathUtil::Stdout(file_name);
        if(FileUtil::IsFileExists(_stdout)) 
            unlink(_stdout.c_str());

        string _stderr = PathUtil::Stderr(file_name);
        if(FileUtil::IsFileExists(_stderr)) 
            unlink(_stderr.c_str());
    }
    // code > 0 : 进程收到了信号导致异常奔溃
    // code < 0 : 整个过程非运行报错(代码为空,编译报错等)
    // code = 0 : 整个过程全部完成
    static string CodeToDesc(int code, const string &file_name)
    {
        string desc;
        switch (code)
        {
        case 0:
            desc = "编译运行成功";
            break;
        case -1:
            desc = "提交的代码是空";
            break;
        case -2:
            desc = "未知错误";
            break;
        case -3:
            // desc = "代码编译的时候发生了错误";
            FileUtil::ReadFile(PathUtil::CompilerError(file_name), &desc, true);
            break;
        case SIGABRT: // 6
            desc = "内存超过范围";
            break;
        case SIGXCPU: // 24
            desc = "CPU使用超时";
            break;
        case SIGFPE: // 8
            desc = "浮点数溢出";
            break;
        default:
            desc = "未知: " + to_string(code);
            break;
        }
        return desc;
    }

    static void Start(const string &in_json, string *out_json)
    {
        Json::Value in_value;
        Json::Reader reader;
        reader.parse(in_json, in_value);

        string code = in_value["code"].asString();
        string input = in_value["input"].asString();
        int cpu_limit = in_value["cpu_limit"].asInt();
        int mem_limit = in_value["mem_limit"].asInt();

        int status_code = 0;
        Json::Value out_value;
        int run_result = 0;
        string file_name;

        if (code.size() == 0)
        {
            status_code = -1;
            goto END;
        }
        file_name = FileUtil::UniqFileName();
        //形成临时src文件
        if (!FileUtil::WriteFile(PathUtil::Src(file_name), code))
        {
            status_code = -2; //未知错误
            goto END;
        }

        if (!Compiler::Compile(file_name))
        {
            //编译失败
            status_code = -3; //代码编译的时候发生了错误
            goto END;
        }

        run_result = Runner::Run(file_name, cpu_limit, mem_limit);
        if (run_result < 0)
        {
            status_code = -2; //未知错误
        }
        else if (run_result > 0)
        {
            //程序运行崩溃了
            status_code = run_result;
        }
        else
        {
            //运行成功
            status_code = 0;
        }
    END:
        out_value["status"] = status_code;
        out_value["reason"] = CodeToDesc(status_code, file_name);
        if (status_code == 0)
        {
            // 整个过程全部成功
            string _stdout;
            FileUtil::ReadFile(PathUtil::Stdout(file_name), &_stdout, true);
            out_value["stdout"] = _stdout;

            string _stderr;
            FileUtil::ReadFile(PathUtil::Stderr(file_name), &_stderr, true);
            out_value["stderr"] = _stderr;
        }
        Json::StyledWriter writer;
        *out_json = writer.write(out_value);
        RemoveTempFile(file_name);
    }
};

compile_server模块

下面借助httplib,实现一个基础版本的服务器,httplib是一个简单的封装好的服务器库,这里直接进行使用:

#include "../common/httplib.h"
#include "compile_run.hpp"
using namespace std;

void Usage(std::string proc)
{
    std::cerr << "Usage: " << "\n\t" << proc << " port" << std::endl;
}

//./compile_server port
int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        return 1;
    }
    httplib::Server svr;

    svr.Post("/compile_and_run", [](const httplib::Request &req, httplib::Response &resp){
        // 用户请求的服务正文是我们想要的json string
        std::string in_json = req.body;
        std::string out_json;
        if(!in_json.empty())
        {
            CompileAndRun::Start(in_json, &out_json);
            resp.set_content(out_json, "application/json;charset=utf-8");
        }
    });
    svr.listen("0.0.0.0", atoi(argv[1]));
    return 0;
}

利用postman尝试进行测试,发现测试是可以通过的,代码正常运行:

在这里插入图片描述

基于MVC结构的OJ服务

什么是MVC?

对于MVC来说,M的意思是Model,通常是和数据进行交互的模块,比如对于题库进行增删改查,而V的意思是View,意思是利用数据进行构建网页和渲染网页内容,展示给用户对应的信息,最后C的意思是Control,控制器控制的是核心业务逻辑

下面的步骤,是要搭建一个小型网站,具体步骤如下:

  1. 获取首页信息
  2. 编辑区域页面
  3. 提交判题功能

下面就基于这些内容,进行代码的编写

用户请求服务路由功能

这个部分内容相对简单,就是借助httplib进行服务的调用

#include <iostream>
#include "../common/httplib.h"

using namespace std;

int main()
{
    httplib::Server svr;

    svr.Get("/all_questions", [](const httplib::Request &req, httplib::Response &resp){
        resp.set_content("这是所有题⽬的列表", "text/plain; charset=utf-8");
    });

    svr.Get(R"(/question/(\d+))", [](const httplib::Request &req, httplib::Response &resp){
        std::string number = req.matches[1];
        resp.set_content("这是指定的⼀道题: " + number, "text/plain; charset=utf-8");
    });

    svr.Get(R"(/judge/(\d+))", [](const httplib::Request &req, httplib::Response &resp){
        std::string number = req.matches[1];
        resp.set_content("指定题⽬的判题: " + number, "text/plain; charset=utf-8");
    });

    svr.set_base_dir("./wwwroot");
    svr.listen("0.0.0.0", 8080);
    return 0;
}

上述只是进行了一个最基础的操作,进行了一个框架的搭建,那么下面就进入MVC的模块

Model模块

Model模块,提供对于数据的一系列操作

对于这个项目的数据来说,最重要的就是题目的一些数据,这里枚举出题目的细节信息:

struct Question
{
    string number;
    string title;
    string star;
    int cpu_limit;
    int mem_limit;
    string desc;
    // 题目预设给用户在线编辑器的代码
    string header;
    // 题目的测试用例,需要和header拼接,形成完整代码
    string tail;
};

实际上,在进行OJ的过程中,基本的逻辑思路很简单,把用户提交上来的代码和测试用例进行拼接,再执行,把执行的结果和测试用例进行比较,即可得到结果,这就是项目的底层逻辑

那么有了这样的思想,实现出Model模块逻辑就很轻松了,下面进行具体的实现过程:

class Model
{
private:
    //题号 : 题目细节
    unordered_map<string, Question> questions;
public:
    Model();
    bool LoadQuestionList(const string &question_list);

    bool GetAllQuestions(vector<Question> *out);

    bool GetOneQuestion(const string &number, Question *q);
    
    ~Model()
    {}
};

view模块

view模块这里采取的是ctemplate中的渲染网页部分,它的基本思想就是实现一个key,value的值替换,达到渲染网页的目的

#pragma once
#include <iostream>
#include <string>
#include <ctemplate/template.h>
#include "oj_model.hpp"
using namespace std;

const string template_path = "./template_html/";

class View
{
public:
    View(){}
    ~View(){}
public:
    void AllExpandHtml(const vector<struct Question> &questions, string *html)
    {
        // 题目的编号 题目的标题 题目的难度
        // 推荐使用表格显示
        // 1. 形成路径
        string src_html = template_path + "all_questions.html";
        // 2. 形成数字典
        ctemplate::TemplateDictionary root("all_questions");
        for (const auto& q : questions)
        {
            ctemplate::TemplateDictionary *sub = root.AddSectionDictionary("question_list");
            sub->SetValue("number", q.number);
            sub->SetValue("title", q.title);
            sub->SetValue("star", q.star);
        }

        //3. 获取被渲染的html
        ctemplate::Template *tpl = ctemplate::Template::GetTemplate(src_html, ctemplate::DO_NOT_STRIP);

        //4. 开始完成渲染功能
        tpl->Expand(html, &root);
    }
    
    void OneExpandHtml(const struct Question &q, string *html)
    {
        // 1. 形成路径
        string src_html = template_path + "one_question.html";

        // 2. 形成数字典
        ctemplate::TemplateDictionary root("one_question");
        root.SetValue("number", q.number);
        root.SetValue("title", q.title);
        root.SetValue("star", q.star);
        root.SetValue("desc", q.desc);
        root.SetValue("pre_code", q.header);

        //3. 获取被渲染的html
        ctemplate::Template *tpl = ctemplate::Template::GetTemplate(src_html, ctemplate::DO_NOT_STRIP);
        
        //4. 开始完成渲染功能
        tpl->Expand(html, &root);
    }
};

Control模块

最后进入Control模块

对于Control模块来说,基本的调用逻辑为:

  1. 把in_json进行反序列化,得到题目id和源代码,input等
  2. 重新拼接用户代码和测试代码,可以得到一个新的代码
  3. 把新的代码给负载最低的主机,发起http请求把结果传输过去
  4. 把结果赋值给out_json即可

选择主机

主机如何选择呢?

在本项目中,提供的方法是使用轮询方式实现的负载均衡,对比所有的主机寻找一个负载最少的进行判断即可

由于要实现负载均衡,所以在进行实际的使用过程中,要对于负载均衡有体现,在实际的运行中,理应拥有几个服务器一起进行负载均衡,那对于这些服务器的管理是有必要的,所以就要先对提供服务的主机进行管理:

// 提供服务的主机
class Machine
{
public:
    string ip;       //编译服务的ip
    int port;        //编译服务的port
    uint64_t load;   //编译服务的负载
    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();
    }

    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;
    }
};

之后,实现负载均衡,由于篇幅的原因这里把最核心的展示出来:

// 选择主机和机器
bool SmartChoice(int *id, Machine **m)
{
    // 1. 使用选择好的主机(更新该主机的负载)
    // 2. 需要可能离线该主机
    mtx.lock();
    // 负载均衡的算法: 轮询
    int online_num = online.size();
    if (online_num == 0)
    {
        mtx.unlock();
        lg(Fatal, "后端所有主机离线");
        return false;
    }
    // 通过遍历的方式,找到所有负载最小的机器
    *id = online[0];
    *m = &machines[online[0]];
    uint64_t min_load = machines[online[0]].Load();
    for (int i = 1; i < online_num; i++)
    {
        // 获取主机的负载,如果负载小就更新
        uint64_t curr_load = machines[online[i]].Load();
        if (min_load > curr_load)
        {
            min_load = curr_load;
            *id = online[i];
            *m = &machines[online[i]];
        }
    }
    mtx.unlock();
    return true;
}

有了这两个模块,就可以去实现出Control模块了,这个是整个业务最核心的模块,它用到了Model模块来提供业务数据,view实现html渲染,还有负载均衡

// 这是我们的核心业务逻辑的控制器
class Control
{
    void RecoveryMachine()
    {
        load_blance_.OnlineMachine();
    }
    //根据题目数据构建网页
    // html: 输出型参数
    bool AllQuestions(string *html);
    bool Question(const string &number, string *html);
    // code: #include...
    // input: ""
    void Judge(const string &number, const string in_json, string *out_json)
    {   
        // 0. 根据题目编号,直接拿到对应的题目细节
        // 1. in_json进行反序列化,得到题目的id,得到用户提交源代码,input
        // 2. 重新拼接用户代码+测试用例代码,形成新的代码
        // 3. 选择负载最低的主机(差错处理)
        // 规则: 一直选择,直到主机可用,否则,就是全部挂掉
        // 4. 然后发起http请求,得到结果
        // 5. 将结果赋值给out_json
    }
};

这样,整个项目的最基础的业务逻辑就使用结束了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1795194.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

UV胶为什么会开裂?如何避免UV胶开裂?

UV胶为什么会开裂&#xff1f;如何避免UV胶开裂&#xff1f; UV胶开裂可能由以下几个主要因素导致&#xff1a; 紫外线照射不足&#xff1a;UV胶的固化需要足够的紫外线能量。如果紫外线照射不足&#xff0c;胶水可能无法完全固化&#xff0c;导致开裂。这可能是由于固化设备…

数据挖掘与机器学习——聚类算法

目录 无监督学习 聚类算法 概念&#xff1a; 功能&#xff1a; 应用场景&#xff1a; 评判标准&#xff1a; 划分聚类&#xff1a; K-means聚类 逻辑实现&#xff1a; 聚类方式 问题&#xff1a; 解决&#xff1a; 可能存在的问题&#xff1a; 1.初始值对K-means聚…

如何理解与学习数学分析——第二部分——数学分析中的基本概念——第10章——实数

第2 部分&#xff1a;数学分析中的基本概念 (Concepts in Analysis) 10. 实数(The Real Numbers) 本章介绍比率数(rational numbers)和非比数(irrational numbers)及其与十进制展开的关系。讨论了实数的公理&#xff0c;并解释了完备性公理对于区分实数和比率数为何必不可少&…

【设计模式】JAVA Design Patterns——Monitor(监视器模式)

&#x1f50d;目的 主要目的是为多个线程或进程提供一种结构化和受控的方式来安全地访问和操作共享资源&#xff0c;例如变量、数据结构或代码的关键部分&#xff0c;而不会导致冲突或竞争条件。 &#x1f50d;解释 通俗描述 监视器模式用于强制对数据进行单线程访问。 一次只允…

体育器材管理系统(Java+MySQL)

技术栈 Java语言&#xff1a;作为主要编程语言&#xff0c;用于编写应用逻辑和界面交互。MySQL数据库&#xff1a;用于存储和管理体育器材的相关数据。Swing窗口视图&#xff1a;用于创建图形用户界面&#xff0c;使用户能够通过窗口进行操作&#xff08;GBK编码&#xff09;。…

MongoDB-4.2.1 之安装和使用

安装 下载安装包 我自己电脑是 Windows7 的老古董&#xff0c;所以就下载老版本的 MongoDB。 mongodb: https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.1.zip 解压安装包到指定路径 我解压到的 C 盘 C:\mongodb-4.2.1 添加环境变量 创建数据库和…

【AIGC X UML 落地】通过多智能体实现自然语言绘制UML图

前天写了篇博文讲到用PlantUML来绘制C类图和流程图。后台有读者留言&#xff0c;问这步能否自动化生成&#xff0c;不想学习 PlantUML 语法。 我想了下&#xff0c;发现这事可行&#xff0c;确实可以做到通过自然语言的描述就能实现 UML图的绘制&#xff0c;昨天晚上加了个班到…

HCIP-Datacom-ARST自选题库_10_其他判断【23道题】

1.端到端时延等于路径上所有处理时延与队列时延之和。 2.部署PPP Multilink之后&#xff0c;数据将根据源地址和目的地址均匀的分配在各条成员链路上。 3.流镜像分为本地流镜像和远程流镜像两种方式。√ 4.IP报文中用Tos字段进行Q0S标记&#xff0c;Tos字段中是使用前6bit来…

《日均70亿请求项目实战》之部署三台zookeeper集群

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

HOW - vscode 使用指南

目录 一、基本介绍1. 安装 VS Code2. 界面介绍3. 扩展和插件4. 设置和自定义 二、常用界面功能和快捷操作&#xff08;重点&#xff09;常用界面功能快捷操作 三、资源和支持 Visual Studio Code&#xff08;VS Code&#xff09;是一款由微软开发的免费、开源的代码编辑器&…

C++之类与类之间的关系

1、UML 2、继承&#xff08;泛化&#xff09; 3、关联 一个类对象与另一个类对象存在一个固定关系。他们的关系不是暂时的&#xff0c;而是固定的。 一个类对象作为另一个类对象的成员。例如订单&#xff0c;是用户的一个成员。用户关联订单。 4、聚合 聚合其实是特殊的一种…

【WP】猿人学_15_备周则意怠_常见则不疑

https://match.yuanrenxue.cn/match/15 抓包分析 抓包分析有一个m参数&#xff0c;三个数字组成 追栈/扣代码 根据启动器顺序追栈&#xff0c;一般优先跳过 jQuery 直接能找到加密函数 每次获取的数字都不一样 window.m function() { t1 parseInt(Date.parse(new Date(…

基于System-Verilog实现DE2-115开发板驱动HC_SR04超声波测距

目录 前言 一、SystemVerilog——下一代硬件设计语言 与Verilog关系 与SystemC关系 二、实验原理 2.1 传感器概述&#xff1a; 2.2 传感器引脚 2.3 传感器工作原理 2.4 整体测距原理及编写思路 三、System-Verilog文件 3.1 时钟分频 3.2 超声波测距 3.3 数码管驱动…

⌈ 传知代码 ⌋ AI驱动食物图像识别

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

Apple开发者macOS描述文件创建

1.选择Profiles然后点击加号创建 2.选择类型为macOS App Development然后点击继续 3.选择描述类型与App ID 然后点击继续 4.选择证书然后点击继续 5.选择设备,然后点击继续 6.输入描述文件后,点击生成 生成成功,点击下载描述文件 下载完成会自动打开描述文件

【西瓜书】4.决策树

1 递归返回情况 &#xff08;1&#xff09;结点包含样本全为同一类别 &#xff08;2&#xff09;属性集为空&#xff0c;没有属性可供划分了 或 有属性&#xff0c;但是在属性上划分的结果都一样 &#xff08;3&#xff09;结点为空结点 **结束时判定该结点的类别遵循如下规则&…

氧兜:新一代隔热防晒膜,打造您的健康氧吧,开启品质生活新篇章

随着人们对生活品质追求的不断提升&#xff0c;氧兜品牌凭借创新科技&#xff0c;为您带来了新一代隔热防晒膜。它不仅具备卓越的隔热防晒功能&#xff0c;更能通过释放负氧离子&#xff0c;为您打造一个居家办公或出行环境的氧吧&#xff0c;让您的生活更加健康、舒适。 一、…

AzSubEnum:针对Azure服务的子域名枚举查询工具

关于AzSubEnum AzSubEnum是一款专门为Azure服务量身定制的子域名枚举查询工具&#xff0c;该工具旨在帮助广大研究人员仔细搜索和识别与各种Azure服务相关的子域名信息。 通过结合查询技术和语句&#xff0c;AzSubEnum能够深入分析Azure的域名架构&#xff0c;并系统地探测和收…

今日arXiv最热大模型论文:大模型都能怎么用?中南大学最新综述:大模型时代的自然语言处理

还记得2022年末ChatGPT的横空出世&#xff0c;带来了整个NLP乃至AI领域的震动&#xff0c;随后如LLaMA、ChatGLM、Qwen等类ChatGPT大模型&#xff08;LLM&#xff09;开始如雨后春笋般涌现&#xff0c;这些先进的模型不仅展示了在零样本学习中的出色表现&#xff0c;还在多种NL…

【ZYNQ】CPU 私有定时器

Zynq 的每个 Cortex-A9 处理器都有自己的专用 32 位定时器和 32 位看门狗定时器&#xff0c;两个处理器共享一个全局 64 位定时器&#xff0c;这些计时器的时钟频率始终为 CPU 频率的 1/2。本文主要介绍 Zynq 芯片 CPU 私有定时器的工作特性&#xff0c;以及私有定时器的基本使…