负载均衡式在线OJ

个人主页:Lei宝啊 

愿所有美好如期而遇


目录

一、所用技术与开发环境

所用技术

开发环境

二、准备及库的安装

1. 升级gcc   (gcc -v查看gcc版本, 超过7就不用看本条升级gcc)

2. 安装 jsoncpp

3. 安装 cpp-httplib 

4. 安装boost库

5. 安装ctemplate

 三、项目宏观结构

1. leetcode结构

2. 项目宏观结构

3. 编写思路

四、compiler服务设计

1. 日志模块

2. 公共方法模块

3. 编译模块

4. 运行模块

5. 整合模块 

6. 主函数

7. 使用Postman进行调试

五、oj_server服务设计

第一个功能:用户请求的服务器路由功能

第二个功能:完成model,提供对数据的操作

第三个功能:control模块,逻辑控制模块

第四个功能:view模块,进行数据渲染

完成control模块,加入负载均衡 

六、效果演示 


一、所用技术与开发环境

所用技术

  • C++ STL 标准库
  • Boost 准标准库(字符串切割)
  • cpp-httplib 第三方开源网络库
  • ctemplate 第三方开源前端网页渲染库
  • jsoncpp 第三方开源序列化、反序列化库
  • 负载均衡设计
  • 多进程、多线程

开发环境

  • ubuntu 5云服务器
  • vscode

二、准备及库的安装

1. 升级gcc   (gcc -v查看gcc版本, 超过7就不用看本条升级gcc)

对于cpp-httplib库来说,用老的编译器,要么编译不通过,要么运行报错。

安装scl来升级gcc

sudo yum install centos-release-scl scl-utils-build

sudo yum install -y devtoolset-7-gcc devtoolset-7-gcc-c++ (这里的7可以是8或者9)

scl enable devtoolset-7 bash (启动,只在本会话有效)

如果想每次登陆的时候,都是较新的gcc,需要把上面的命令添加到你的~/.bash_profile中

添加:scl enable devtoolset-7 bash

2. 安装 jsoncpp

sudo yum install -y jsoncpp-devel

3. 安装 cpp-httplib 

建议:cpp-httplib 0.7.15, 码云上去找。

4. 安装boost库

sudo apt update 更新软件包

sudo apt install libboost-all-dev(博主是unbuntu)

5. 安装ctemplate

这个资源可以在这里找:https://hub.fastgit.xyz/OlafvdSpek/ctemplate 

接着将资源放到服务器上,执行如下命令

./autogen.sh

./configure

make //编译

make install //安装到系统中 如果报错,加上sudo

 三、项目宏观结构

  1. comm 模块
  2. compiler模块
  3. oj_server模块

1. leetcode结构

本项目只实现了leetcode的题目列表和刷题功能。

2. 项目宏观结构

B/S模式:浏览器-服务器(Browser-Server, B/S)模式

3. 编写思路

先写compiler服务器,接着写oj_server,最后是前端页面设计,直接copy。

四、compiler服务设计

1. 日志模块

其他模块都会引用这个模块,这个模块我们之前的文章有详细代码和讲解,我们这里不多赘述,贴出链接:日志介绍及简单实现

2. 公共方法模块

这个模块可以先直接跳过不看,当后面模块用到这个模块中的方法时返回来看。

这里介绍几个函数:gettimeofday获取时间戳,stat获取文件属性(能获取就能说明文件存在,获取不到就说明文件不存在);C++11及以上版本,支持在atomic头文件中,我们使用atomic_int类型定义的变量,有原子加、原子减、原子比较并交换、原子自增、原子自减等操作。

#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <atomic>
#include <fstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
using namespace std;

namespace ns_commfunc
{
    const string temp = "./temp/";

    // 将文件名变为文件路径,并且添加后缀
    class PathUtil
    {

    public: 
        static string AddSuffix(const string filename, const string suffix)
        {
            string ret = temp + filename + suffix;
            return ret;
        }

        //编译模块
        static string filepath_src(const string& filename)
        {
            return AddSuffix(filename, ".cpp");
        }

        static string filepath_exe(const string& filename)
        {
            return AddSuffix(filename, ".exe");
        }

        static string filepath_stderr(const string& filename)
        {
            return AddSuffix(filename, ".compile_err");
        }

        //运行模块
         static string filepath_stdin(const string& filename)
        {
            return AddSuffix(filename, ".stdin");
        }

        static string filepath_stdout(const string& filename)
        {
            return AddSuffix(filename, ".stdout");
        }

        static string filepath_stderr_runner(const string& filename)
        {
            return AddSuffix(filename, ".stderr");
        }

    
    };

    class TimeUtil
    {
    public:
        static string Gettimeofms()
        {
            struct timeval tv;
            gettimeofday(&tv, nullptr);
            string ret = to_string(tv.tv_sec * 1000 + tv.tv_usec / 1000);

            return ret;
        }
    };

    class FileUtil
    {
    public:

        static bool Isexist_file(const string filename)
        {
            struct stat temp;
            int ret = stat(filename.c_str(), &temp);

            return ret == 0 ? true : false;
        }

        static string UniqueFilename()
        {
            static atomic_uint id(0);
            id++;

            return TimeUtil::Gettimeofms() + "_" + to_string(id);
        }

        static bool WritecodeToFile(const string filename, const string& content)
        {
            ofstream out(filename);
            if(!out.is_open()) return false;

            out.write(content.c_str(), content.size());
            return true;
        }

        static bool Readfile(const string& filename, string& content, bool keep)
        {
            ifstream in(filename);
            if(!in.is_open()) return false;

            string line;
            while(getline(in, line))
            {
                content += line;
                content += keep ? "\n" : "";
            }

            return true;
        }

        static string CodeTodesc(int status, string filename)
        {
            string ret = "";

            switch (status)
            {
            case 0:
                ret = "运行正常";
                break;
            case -1:
                ret = "代码为空";
                break;
            case -2:
                ret = "未知错误";
                break;
            case -3:
                // ret = "编译错误";
                FileUtil::Readfile(filename, ret, true);
                break;
            case 24:
                ret = "超时";
            default:
                ret = "待填充";
                break;
            }

            return ret;
        }


    };
}

3. 编译模块

1. 创建编译类Compile,封装在命名空间ns_compile中:

namespace ns_compile
{
    class Compile
    {
    private:
    public:
        Compile()
        {}

        ~Compile()
        {}
    };
}

2. 写一个方法,完成编译功能,那么这个方法的参数和返回值我们如何设置?我们要明白,外面有一个整合模块来接收请求,并且从请求中提取代码并解析成字符串形成源文件,也就是说,我们的编译方法,参数为这个文件名,也就是字符串类型。返回值可以设置为bool类型,表示是否编译成功。

       /*
            @return val: 编译结果是否成功
            @pragma    : 文件名
        */
        static bool compile(const string& filename)
        {}

接下来就可以创建子进程,按照我们上面的逻辑写代码了:

其中,我们在comm文件夹下,创建了一个公共方法文件,这个文件中我们写不同模块需要使用的公共方法,这些方法封在ns_commfunc中的不同类中,有Path类,这个类中的方法用来形成文件路径;文件类,这个类中的方法用来对文件进行操作。

static bool compile(const string& filename)
        {
            int id = fork();
            if(id < 0)
            {
                Log(ERROR, "子进程创建失败");
                return false;
            } 
            else if(id == 0)
            {
                //子进程

                //重定向
                umask(0);
                i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lei宝啊

觉得博主写的有用就鼓励一下吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值