C++实战演练---负载均衡在线oj项目 || 编译服务模块设计

news2025/1/16 8:45:16

顾得泉:个人主页

个人专栏:《Linux操作系统》 《C++从入门到精通》  《LeedCode刷题》

键盘敲烂,年薪百万!


前言

       上一篇预热文章简单说过我们的编写思路,首先要实现的是编译功能,那么在编译功能版块中,我们首先要考虑一些问题:

     1.编译出来的临时文件我们应该放在哪里?

     2.每次编译出来的临时文件名称重复如何解决?

     3.编译正确与否的相关信息在哪显示?

注:博客中项目相关代码展示不完整!!!


一、comm模块

       为了解决上述问题,我们首先列出一个公共模块,里面存放两个主要功能的文件:

       1.<util.hpp>: 生成存放临时文件的路径,生成各个文件唯一的文件名

       2.<log.hpp>: 生成开放式日志接口,生成编译相关信息

1.util.hpp

 1.拼接成固定临时文件路径

       我们希望用户代码及其编译的临时文件都存放在temp文件夹下,所以我们可以设计一下拼接成文件完整路径的方法,定义在PathUtil类中,它所提供的方法都设置为public的静态方法,供外部直接调用。

class PathUtil
{
public:
    static std::string AddSuffix(const std::string &file_name, const std::string &suffix)
    {
        std::string path_name = temp_path;
        path_name += file_name;
        path_name += suffix;
        return path_name;
    }
    //编译时需要的临时文件
    //构建源文件路径+后缀的完整文件名
    //1234 -> ./temp/1234.cpp
    static std::string Src(const std::string &file_name)
    {
        return AddSuffix(file_name, ".cpp");
    }
    //构建可执行程序的完整路径+后缀名
    static std::string Exe(const std::string &file_name)
    {
        return AddSuffix(file_name, ".exe");
    }
    static std::string CompilerError(const std::string &file_name)
    {
        return AddSuffix(file_name, ".compiler_error");
    }

    //运行时需要的临时文件
    static std::string Stdin(const std::string &file_name)
    {
        return AddSuffix(file_name, ".stdin");
    }
    static std::string Stdout(const std::string &file_name)
    {
        return AddSuffix(file_name, ".stdout");
    }
    //构建该程序对应的标准错误完整的路径+后缀名
    static std::string Stderr(const std::string &file_name)
    {
        return AddSuffix(file_name, ".stderr");
    }
};

2.获取秒级时间戳

       为了下面的日志功能做铺垫,因为我们希望打印出来的文件名各不相同。

class TimeUtil
{
public:
    static std::string GetTimeStamp()
    {
        struct timeval _time;
        gettimeofday(&_time, nullptr);
        return std::to_string(_time.tv_sec);
    }
    //获得毫秒时间
    static std::string GetTimeMs()
    {
        struct timeval _time;
        gettimeofday(&_time, nullptr);
        return std::to_string(_time.tv_sec * 1000 + _time.tv_usec / 1000);
    }
};
static std::string UniqFileName()
{
    std::atomic_uint id(0);
    id++;
    //毫秒级时间戳+原子性递增唯一值:保证唯一性
    std::string ms = TimeUtil::GetTimeMs();
    std::string uniq_id = std::to_string(id);
   
    return ms + "." + uniq_id;
}

2.log.hpp

常见日志等级包括:

  1. DEBUG(调试):用于调试目的的详细日志信息,通常不在生产环境中启用。

  2. INFO(信息):用于提供一般信息的日志,例如应用程序的启动、关闭、关键操作等。

  3. WARNING(警告):用于表示可能的问题或潜在的错误,但不会导致应用程序的终止或异常。

  4. ERROR(错误):用于表示错误或异常情况,可能会导致应用程序的中止或异常。

  5. FATAL(致命错误):用于表示严重的错误或不可恢复的情况,可能会导致应用程序的崩溃或无法继续运行。

实现简单的日志接口:

// 日志等级
enum
{
    INFO, //就是整数
    DEBUG,
    WARNING,
    ERROR,
    FATAL
};

inline std::ostream &Log(const std::string &level, const std::string &file_name, intline)
{
    // 添加日志等级
    std::string message = "[";
    message += level;
    message += "]";

    // 添加报错文件名称
    message += "[";
    message += file_name;
    message += "]";

    // 添加报错行
    message += "[";
    message += std::to_string(line);
    message += "]";

    // 日志时间戳
    message += "[";
    message += TimeUtil::GetTimeStamp();
    message += "]";

    // cout 本质 内部是包含缓冲区的
    std::cout << message; //不要endl进行刷新

    return std::cout;
}
// LOG(INFo) << "message" << "\n";
// 开放式日志
#define LOG(level) Log(#level, __FILE__, __LINE__)

二、编译服务整体架构

       这个模块只负责进行代码编译,意味着我们目前默认是能够接收远端提交的代码文件,并且这个文件对于我们编译模块来说是一个临时文件,编译成功后也会形成一个临时的可执行文件,这些文件的存放位置已经解决,那么现在就开启编译之路。


三、编译服务主体

       现在我们要做的就是把用户提交的代码进行编译形成可执行程序,对结果并进行展示。程序运行无非就是三种结果:

       1.代码跑完,结果正确

       2.代码跑完,结果不正确

       3.代码没跑完,发生异常了

static bool Compile(const std::string &file_name)
{
    pid_t pid = fork();
    if(pid < 0)
    {
         LOG(ERROR) << "内部错误,创建子进程失败" << "\n";
         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)
         {
               LOG(WARNING) << "没有成功形成stderr文件" << "\n";
               exit(1);
         }
         //重定向标准错误到_stderr
         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(), "-std=c++11", nullptr/*不要忘记*/);
         LOG(ERROR) << "启动编译器g++失败,可能是参数错误" << "\n";
         exit(2);
    }
    else
    {
         waitpid(pid, nullptr, 0);
         //编译是否成功,就看有没有形成对应的可执行程序
         if(FileUtil::IsFileExists(PathUtil::Exe(file_name)))
         {
              LOG(INFO) << PathUtil::Src(file_name) << "编译成功" << "\n";
              return true;
         }
    }
    LOG(ERROR) << "编译失败,没有形成可执行程序" << "\n";
    return false;
}

       调用dup2系统调用的时机在调用execlp之前,也就是说我们在程序替换前完成了重定向,而重定向不会影响进程已经打开的文件,也就是不影响子进程现存的文件描述符表,那么g++编译的报错信息就合乎预期地打印到了我们重定向的文件中了。


四、功能测试加其他

1.mkaefile助手

compile_server:compile_server.cc
	g++ -o $@ $^ -std=c++11 -ljsoncpp -lpthread
.PHONY:clean
clean:
	rm -f compile_server

2.测试

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

    svr.Get("/hello",[](const Request &req, Response &resp)
    {
        //用来进行基本测试
        resp.set_content("hello httplib,你好", "text/plain; charset=utf-8");
    });
    
    svr.Post("/compile_and_run", [](const Request &req, 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;
}

测试过程:

测试结果:


结语:关于项目本次的这里就结束了,如果大家有什么问题,欢迎大家在评论区留言~~~ 

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

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

相关文章

好消息|NIH给博士后加薪,起步年薪6.1万美元

近日美国国立卫生研究院&#xff08;NIH&#xff09;正式宣布提高其资助的博士后年薪为61,008美元并根据经验资历进一步向上调整。同时NIH承诺未来3-5年内将博士后的最低年薪提高到70000美元。对于有望从事博士后研究的科研人员而言&#xff0c;这是一个好消息&#xff0c;为此…

水面垃圾监测识别摄像机

随着城市化进程的加快和旅游业的兴起&#xff0c;水域环境污染问题日益突出&#xff0c;水面垃圾成为环境保护的重要问题。为有效监测和清理水面垃圾&#xff0c;水面垃圾监测识别摄像机应运而生。水面垃圾监测识别摄像机利用高清晰度摄像头和智能识别算法&#xff0c;能够实时…

springcloud简单了解及上手

springcloud微服务框架简单上手 文章目录 springcloud微服务框架简单上手一、SpringCloud简单介绍1.1 单体架构1.2 分布式架构1.3 微服务 二、SpringCloud与SpringBoot的版本对应关系2022.x 分支2021.x 分支2.2.x 分支 三、Nacos注册中心3.1 认识和安装Nacos3.2 配置Nacos3.3 n…

BM11 链表相加(二)

描述 假设链表中每一个节点的值都在 0 - 9 之间&#xff0c;那么链表整体就可以代表一个整数。 给定两个这种链表&#xff0c;请生成代表两个整数相加值的结果链表。 数据范围&#xff1a;0≤&#x1d45b;,&#x1d45a;≤10000000≤n,m≤1000000&#xff0c;链表任意值 0≤…

centos7.9安装PHP运行环境

MySQL安装 报错&#xff1a;源 "MySQL 8.0 Community Server" 的 GPG 密钥已安装&#xff0c;但是不适用于此软件包。请检查源的公钥 URL 是否配置正确。 解决&#xff1a; yum install mysql-server -y --nogpgcheck 查询初始密码 grep temporary password /var…

SpringBoot实现 QQ邮箱验证码

SpringBoot实现 QQ邮箱验证码 文章目录 SpringBoot实现 QQ邮箱验证码一、开通SMTP校验码1.登录qq邮箱2.开启SMTP服务3.发送手机短信4.得到授权码 二、回到spring项目中1.导入所需依赖2.验证码工具类3.具体实现逻辑(serviceimpl) 三、测试qq邮箱验证码 一、开通SMTP校验码 1.登…

基于Java+SpringBoot+vue+elementui 实现猜灯谜答题抽奖系统

目录 系统简介效果图1、手机答题端2、后台系统管理 源码结构源码下载地址技术交流 博主介绍&#xff1a; 计算机科班人&#xff0c;全栈工程师&#xff0c;掌握C、C#、Java、Python、Android等主流编程语言&#xff0c;同时也熟练掌握mysql、oracle、sqlserver等主流数据库&…

Linux环境变量详解

文章目录 1. 前言2 什么是PATH环境变量3. 如何添加PATH环境变量4. 系统中的其他环境变量5. 环境变量的由来6. 环境变量的基本操作6.1 设置环境变量6.2 通过getenv获取环境变量6.3 环境变量的应用场景 7. 通过命令行参数获取环境变量 1. 前言 本篇文章将以PATH环境变量为例来对整…

CSP备考---位运算

前言 本期我们将学习位运算&#xff0c;与本期类型的考点&#xff08;二进制转换&#xff09;反码、补码、原码。 1、位运算是什么 首先我们需要先了解位运算是什么。 我们知道&#xff0c;计算机中的数在内存中都是以二进制形式进行存储的 &#xff0c;而位运算就是直接对整…

J-STAGE (日本电子科学与技术信息集成)数据库介绍及文献下载

J-STAGE (日本电子科学与技术信息集成)是日本学术出版物的平台。它由日本科学技术振兴机构&#xff08;JST&#xff09;开发和管理。该系统不仅包括期刊&#xff0c;还有论文集&#xff0c;研究报告、技术报告等。文献多为英文&#xff0c;少数为日文。目前网站上所发布的内容来…

通过windows远程桌面,远程连接CentOS系统

1.配置阿里云的YUM仓库 1.1 备份当前的YUM仓库配置文件 sudo cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup1.2 下载阿里云的CentOS仓库配置文件 对于CentOS 7&#xff1a; sudo wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirr…

Java RMI反序列化总结篇-01

1.java rmi反序列化 RMI 允许一个应用程序访问另外一个服务器或虚拟机上的对象&#xff0c;方法和服务&#xff0c;它使远程方法调用就像在本地调用一样简单。它为用户屏蔽了底层的网络传输细节&#xff0c;使用的时候只需适当处理异常即可。所以 RMI 是非常容易使用的&#x…

uniapp开发安卓app使用文字转语音技术

在 UniApp 开发安卓应用时&#xff0c;要实现文字转语音&#xff08;Text-to-Speech, TTS&#xff09;技术&#xff0c;你可以利用 UniApp 的跨平台能力结合原生模块或第三方服务来实现。以下是一些建议的步骤和方法&#xff1a; 1. 使用 UniApp 原生模块&#xff08;如果支持…

Linux中的磁盘分析工具ncdu

2024年5月14日&#xff0c;周二上午 概述 ncdu 是一个基于文本的用户界面磁盘使用情况分析工具。它可以在终端中快速扫描目录&#xff0c;并统计该目录下的文件和文件夹的磁盘使用情况&#xff0c;以交互友好的方式呈现给用户。 安装 在 Debian/Ubuntu 系统下&#xff0c;可…

Linux的命名管道 共享内存 信号量

目录 命名管道 mkfifo函数 unlink函数 命名管道类 服务端 客户端 共享内存 shmget函数 ftok函数 key和shmid的区别 snprintf函数 ipcs指令 ipcrm指令 shmctl函数 shmat函数 void*做返回值 创建共享内存空间 服务端 客户端 信号量 命名管道 基本概念&a…

Unity使用sherpa-onnx实现离线语音合成

sherpa-onnx https://github.com/k2-fsa/sherpa-onnx 相关dll和lib库拷进Unity&#xff0c;官方示例代码稍作修改 using SherpaOnnx; using System; using System.IO; using System.Runtime.InteropServices; using UnityEngine;public class TTS : MonoBehaviour {public st…

基于java 自定义注解Annotation设计简单ORM框架——进阶篇

目录 引言实例新建两个注解标注实体类拼接sql语句 总结 引言 一般Java规范编程&#xff08;只是一种习惯&#xff0c;而不是强制&#xff09;中&#xff0c;变量的命名方式一般采用驼峰式命名。比如userName&#xff0c;userImage。但是在数据库中一般不会采用驼峰式&#xff…

工业物联网解决方案:机房动环监控系统

工业物联网解决方案&#xff1a;机房动环监控系统 工业物联网&#xff08;IIoT&#xff09;作为数字化转型的关键驱动力&#xff0c;正深刻改变着各行各业的运作模式&#xff0c;其中机房动环监控系统是实现智能化运维管理的重要组成部分。该系统通过集成传感器技术、大数据分…

位图(c++)

文章目录 1.位图概念2.位图的实现3.应用&#xff08;解决整形存在或次数问题&#xff09;3.1存在问题3.2次数问题 5.搜索的方法对比&#xff1a; 1.位图概念 和哈希一样&#xff0c;都是一个表来记录某个元素的个数或者存在与否&#xff1b;不同的是哈希使用的计算机定义的完整…

跨境必看|TikTok账号运营的八大秘籍

国内的传统生意都是可以在抖音上做&#xff0c;那么也可以在TikTok 上重新做一遍。那该如何才能把握住这片巨大的蓝海&#xff0c;TikTok 账号的运营就成为了主要的关键了&#xff0c;对于TikTok账号运营的八大秘籍&#xff0c;大家一起看看是如何做的&#xff1f; 一、固定节…