# cloud_code_evaluation_platform **Repository Path**: voice-of-sentiment/cloud_code_evaluation_platform ## Basic Information - **Project Name**: cloud_code_evaluation_platform - **Description**: 基于负载均衡的云端代码评测平台 - **Primary Language**: C++ - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-07-11 - **Last Updated**: 2024-04-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 云端代码评测平台 --- ## 1、项目宏观原理 ![image-20230711155444658](image/image-20230711155444658.png) * 客户端(browser):向服务器发出请求,比如请求题目列表,请求特定题目的编写,或者是提交代码 * 服务器:oj_server模块接收到browser发过来的请求之后,根据不同的需求来决定相应的操作,如果请求是题目列表或者是题目的编写,那么oj_server模块去访问文件或者是数据库将需要的信息返回给browser,如果是提交代码的操作,那么oj_server模块会使用负载均衡的技术,根据每个complile_server的负载情况,将编译代码和运行代码的工作发布给不同的complile_server来保证每个complile_server的资源消耗情况差不多,当complile_server执行完成之后,将代码的执行结果返回给oj_server,然后oj_server再返回给browser ## 2、项目宏观结构模块 项目的核心模块有三个 1. comment:公共模块 2. compile_server:编译与运行模块 3. oj_server:获取题目列表,查看编写题目界面,负载均衡,其他功能 项目具体功能实现leetcode的题目列表+在线编程功能 ## 3、在线oj技术栈和项目环境 - 后端:C/C++、C++11、STL、准标准库Boost、Jsoncpp、cpp-httplib、ctemplate、负载均衡设计、多进程、多线程、MySQL C connect - 前端:html5、css、js、jQuery、Ajax、Ace前端在线编辑器 - 项目环境:Centos 7.6云服务器、vim、gcc(g++)、Makefile、visual studio code、MySQL workbench ## 4、compile_server模块的编写 compile_server模块提供的服务::编译并运行代码,得到格式化的相关的结果 ![image-20230711162239309](image/image-20230711162239309.png) ### commen模块的日志功能编写 ``` 这个模块设置了几个日志等级,不同的等级代表的是程序执行的不同状态 通过宏定义的方式,将log函数进行封装,使用了__FILE__和__LINE__来获取执行文件名和调用行数,使其使用起来更加方便 同时通过返回ostream&的对象,来是这个日志达到一种开放式的效果,可以自己决定日志后面添加什么内容 ``` ### compiler编写:只负责代码编译功能 ``` 通过传入文件的名字,来对文件进行编译 由于我们编译的时候,会生成许多的临时文件,这些临时文件需要有后缀,所以我们会将这个文件根据不同的场景进行后缀的添加 然后我们是通过fork子进程,让子进程来进行编译的工作,而父进程等待子进程的退出 子进程在对文件进行编译之前需要创建一个compiler_error为后缀的文件,这个文件用来保存编译出错的相关信息 当然我们需要把标准错误重定向到compiler_error中 然后我们执行execlp来进行进程替换,替换成g++编译器对这个文件的编译 因为我们把标准错误重定向过,所以如果生成了编译错误的信息,就会往compiler_error的文件写入 父进程等待子进程退出成功之后,会对exe后缀的文件进行是否存在的判断,通过stat函数来进行判断的 如果文件存在那么就返回成功,如果不存在就返回失败 ``` ### runner编写:只负责程序运行功能 ``` 通过传入文件的名字,来对程序进行运行,通过传入rlimit来对程序的运行产生限制 对于一个程序执行来说,可以有数据的输入(stdin),数据的输出(stdout),(stderr) 由于我们运行的时候,会生成许多的临时文件,这些临时文件需要有后缀,所以我们会将这个文件根据不同的场景进行后缀的添加 所以我们需要这些数据,我们创建对应的文件 然后我们是通过fork子进程,让子进程来进行运行的工作,而父进程等待子进程的退出 子进程通过dup2函数进行重定向 这样程序的各种运行结果我们都可以通过文件来查看了 但是我们不能让一个子进程毫无顾虑的执行下去,我们需要对这个进程的执行进行一定的限制 我们这里限制了进程执行的时长和进程消耗的内存大小 然后我们再进行execl进行进程的替换,替换为我们想要执行的进程 子进程如果正常退出,就没有收到信号,如果是异常退出,那么就是收到了信号 父进程就等待子进程的退出,然后获取到子进程的信号码,进行返回 ``` ### compile_run编写:将compiler和runner带有一定业务逻辑的运行 编写这个模块之前我们需要安装jsoncpp 使用如下的命令就可以进行安装了 ```cmd sudo yum install -y jsoncpp-devel ``` ``` 通过网络服务拿到的in_json串,对in_json串进行反序列化,拿到里面的字段,进行相应的处理,将处理好的结果通过序列化形成out_json返回给网络服务 首先我们制定了in_json串里面会有那些内容 code:用户提交的代码 input:用户给自己提交的代码对应的输入,不做处理 cpu_limit:时间要求 mem_limit:空间要求 我们拿到用户的代码之后,我们需要形成一个唯一的文件名来保存用户的代码,我们使用毫秒时间戳+原子性递增唯一值:来保证文件名的唯一性 然后将用户的代码写入到唯一的文件中 然后就可以执行compiler来编译代码了 接着编译好之后,可以执行run运行可执行程序了,这里可以传入时间要求和空间要求 最后程序运行完毕之后,我们拿着run运行完毕的返回值 进行out_json的填充 必填: status_code:状态码 reason:本次请求结果 选填: stdout:用户程序运行完的结果 stderr:程序运行完的错误结果 这里的状态码就是run运行的返回值,填入状态码 然后调用code_to_desc,根据不同的返回值,拿到不同的结果描述 上面的两个是必填的,如果程序运行成功了,那么也可以拿到stdout文件和stderr文件的内容 然后将所有的内容写入到out_json串中 最后调用remove_temp_file移除所有生成的临时文件 ``` ### compile_server编写:将编译运行的功能打包成网络服务 #### 安装cpp-httplib 编写这个模块之前需要安装cpp-httplib库 下载地址如下:[cpp-httplib](https://gitee.com/welldonexing/cpp-httplib/tags?page=3) 我们使用0.7.15的版本,这个版本相对稳定一点,如果是最新版本可能对编译的要求比较高 ![image-20230518220323743.png](https://s2.loli.net/2023/05/20/YSBCTrjHvZuexqE.png) 下载好压缩包,上传到服务器解压之后得到如下的文件,而我们主要使用的是httplib.h,要使用这个库非常简单,只需要拷贝到你需要使用的目录下就可以了 ![image-20230518220656208.png](https://s2.loli.net/2023/05/20/vGtoVHeuwPYxTWN.png) #### 升级gcc编译器 使用cpp-httplib库的时候,需要较新版本的gcc编译器 用老的编译器,要么编译不通过,要么直接运行报错 我们centos 7默认gcc版本是4.8.5 ![image-20230518220906063.png](https://s2.loli.net/2023/05/20/pg4ByEUGz2DQwR9.png) gcc的升级 首先安装scl ```cmd sudo yum -y install centos-release-scl scl-utils-build ``` 然后安装新版本的gcc ```cmd sudo yum -y install devtoolset-7-gcc devtoolset-7-gcc-c++ #改变中间的数字,可以安装不同版本的gcc编译器 #注意devtoolset是一个工具集,如果版本太高了可能会导致有一些程序运行异常 #比如我使用devtoolset-11的工具集就会导致man手册无法使用,换回了devtoolset-9就可以正常使用了 ``` 查看是否安装成功 ![image-20230518221823851.png](https://s2.loli.net/2023/05/20/r85JliWxsN9UDz1.png) 使用命令进行新版本的启动 ```cmd scl enable devtoolset-7 bash ``` 可以看到已经是7.3.1版本了 ![image-20230518221948446.png](https://s2.loli.net/2023/05/20/AwDcoMhmiv789pe.png) 但是命令行启动只有在本次对话窗口有效,如果新启对话窗口,那么就还是原来的版本 ![image-20230518220906063.png](https://s2.loli.net/2023/05/20/pg4ByEUGz2DQwR9.png) 如果不想每次都输入指令来启动新版本的gcc 可以在~/.bash_profile文件里面加入如下的指令 ```profile scl enable devtoolset-7 bash ``` ![image-20230518222507044.png](https://s2.loli.net/2023/05/20/G2JZ5vxWSnH3Vil.png) #### 使用postman模拟网络请求 我们还需要准备一个postman软件用来模拟发送json串的网络请求 下载网址:[Postman](https://www.postman.com/) 安装没有什么需要注意的点 然后按照如下的方式操作 ![image-20230714190416060](image/image-20230714190416060.png) 我们可以看到我们程序返回的json串在下面完整的显示出来了,发送的in_json串如下 ```json { "code" : "#include \nint main(){std::cout << \"hello postman!\" << std::endl;\nreturn 0;}", "input" : "", "cpu_limit" : 3, "mem_limit" : 500000 } ``` #### compile_server模块的描述 ```mysql 这个模块,我们是把我们的compile_run模块形成一个网络服务 当我们的服务器接收到http请求,然后里面的方法使用是POST方法的时候 我们提取这个http请求的body部分,里面就是我们compile_run模块需要使用的in_json 然后我们再定义一个out_json,用来接收compile_run运行之后填入的out_json 然后我们构建http响应返回过去 ``` 对于整个compile_server模块的调用逻辑如下所示 ![image-20230718220610282](image/image-20230718220610282.png) ## 5、oj_server模块的编写:基于MVC结构的oj服务设计 ### 什么是mvc ``` Model(模型):模型层表示应用程序中的数据。 它负责处理数据的存储和访问,以及数据的验证和处理。通常是和数据进行交互的模块,比如对我们的题库进行增删改查 View(视图):视图层是用户界面,负责将模型层的数据呈现给用户。视图通常包括HTML、CSS、JavaScript等前端技术,用于渲染用户界面并与用户交互。视图的目的是提供一种用户友好的方式来显示和操作数据。通常是拿到model的数据之后,要进行构建网页,渲染网页内容,展示给用户的(浏览器) Controller(控制器):负责协调模型层和视图层之间的交互。控制器接收用户请求,处理业务逻辑,并更新模型层的数据。控制器还负责将模型层的数据传递给视图层,并将用户输入传递给模型层。控制器的目的是将用户请求转换为模型层的操作,并将模型层的数据呈现给视图层。 ``` ### version1:文件版题目设计 在我们正式编写基于MVC结构的oj服务之前,我们需要有一个题库,这样我们才能写model模块,我们现在实现的第一个版本的题库,是基于文件来做的 我们的文件版题目的设计方式如下 在oj_server的目录下创建一个quesions文件夹,里面就存档的是所有的题目信息 ![image-20230716192020935](image/image-20230716192020935.png) 进入questions目录,由两部分组成一个是questions.list,一个是题目所在的具体文件夹,他们之间是通过题目的编号来产生联系的 ![image-20230716192231702](image/image-20230716192231702.png) questions.list,从左往右依次存放的是 * 题目的编号 * 题目的标题 * 题目的难度 * 题目的描述,题面 * 时间要求(内部处理) * 空间要求(内部处理) ![image-20230716192447097](image/image-20230716192447097.png) 进入题目所对应的目录,由三个部分构成 ![image-20230716192723117](image/image-20230716192723117.png) * desc.txt:用来存放题目的具体描述 ![image-20230716192921617](image/image-20230716192921617.png) * header.cpp:题目预设给用户在线编辑器的代码 ![image-20230716193044757](image/image-20230716193044757.png) * tail.cpp:题目的测试用例,需要和header拼接,形成完整代码 ![image-20230716193246627](image/image-20230716193246627.png) ### version2:MySQL版题目设计 #### mysql库函数的安装 要使用C语言连接mysql,需要使用mysql官网提供的库,可以去官网[MySQL Community Downloads](https://dev.mysql.com/downloads/)下载 ![image-20230720231003274](image/image-20230720231003274.png) ![image-20230720231022609](image/image-20230720231022609.png) ![image-20230720231454384](image/image-20230720231454384.png) 下载好然后上传到Linux上,解压后我们只需要如下的文件 ![image-20230720231514688](image/image-20230720231514688.png) Include里面是头文件 ![image-20230720231527494](image/image-20230720231527494.png) 我们需要将这些头文件拷贝到Linux下的系统头文件的路径下 首先创建一个mysql文件夹,然后将include里面的文件拷贝到mysql文件夹里面 ![image-20230720231625492](image/image-20230720231625492.png) Lib里面是动态库 ![image-20230720231633256](image/image-20230720231633256.png) 我们需要将这些动态库拷贝到Linux下的系统动态库的路径下 注意lib中的软连接需要进行手动连接 ![image-20230720231647520](image/image-20230720231647520.png) ![image-20230720231651775](image/image-20230720231651775.png) #### 添加oj_server用户到MySQL中,并完成表的创建 添加oj_server用户到MySQL中 ![image-20230720232744129](image/image-20230720232744129.png) 创建oj_questions数据库,这个就是存放题目的数据库 ![image-20230720233022397](image/image-20230720233022397.png) 修改oj_server的权限,给予它oj_questions这个数据库的所有权限 ![image-20230720233508498](image/image-20230720233508498.png) 然后我们使用Workbench远程连接mysql来进行图形化的操作,可以去官网[MySQL Community Downloads](https://dev.mysql.com/downloads/)下载 ![image-20230720233826545](image/image-20230720233826545.png) ![image-20230720233841534](image/image-20230720233841534.png) 安装好之后,进入软件的界面,点击+号,进行数据库的连接 ![image-20230720233928389](image/image-20230720233928389.png) 填入好对应的IP和端口,即可开始进行连接了 ![image-20230720234143876](image/image-20230720234143876.png) 然后我们可以根据我们之前设计好的结构体,来设计表的结构 ![image-20230721001303115](image/image-20230721001303115.png) 随后就是给数据库录入题目了 ![image-20230721001351268](image/image-20230721001351268.png) #### oj_model_mysql模块的描述 ``` 因为这个模块我们是从数据库里面拿去数据,所以我们这个模块不需要提前把数据给保存在一个vector里面 get_all_questions和get_one_question都是提前设置好sql语句的部分,只不过get_one_question需要再次拼接一下题目的编号,然后调用query_mysql来获取数据库里面的数据 query_mysql就是调用指定的mysql接口连接数据库,然后执行传入的sql语句,通过mysql_fetch_row拿到一行的数据,通过循环不断地拿到每一行的数据,拿到结果之后进行解析,之后往传入的vector之中进行push_back,就能拿到所查找的数据 ``` ### oj_model_file编写:提供对数据的操作 ``` 我们实现的第一个版本题目是以文件的形式进行保存的 这个模块的主要目的就是为了获取文件里面的题目的具体信息 我们这个模块会有一个std::unordered_map _question 里面保存的是题目编号和题目的具体信息之间的映射关系 首先是load_questions_list函数,将questions.list进行读取 然后根据questions.list里面的内容,将每一行当做一个一个题目 使用boost库中的split函数对每一行进行切分,获取到每一个题目的各种信息 然后把这些信息填入到question结构体中,然后根据获取到的题目的编号 读取题目对应的desc.txt,header.tail.cpp文件的内容到question结构体中 最后将question插入到_question中去 这个函数的功能相对于把题库中的所有题目加载到内存中去,以供我们后续去使用 get_all_questions函数,将_question里面的所有题目信息,保存到一个vector中去,这里需要对获得的题目,按照number进行升序排列,保证获取的题目列表是有序的 get_one_question函数,根据特定的题目编号,将特定的题目的信息保存到question结构体中去 ``` ### oj_view编写:对数据进行渲染,显示给用户 #### 安装ctemplate 编写这个模块之前,我们需要安装ctemplate库 github的下载地址如下 [ctemplate](https://github.com/OlafvdSpek/ctemplate) 下载好之后,进入相应的目录下,执行如下的命令 ```cmd ./autogen.sh && ./configure make make install ``` 可能会出现一下的错误 ![image-20230716195557604](image/image-20230716195557604.png) ![image-20230716195936428](image/image-20230716195936428.png) 我们需要下载如下的工具 ``` sudo yum install autoconf automake libtool ``` 很可能出现如下的错误 ``` ./configure: line 4469: syntax error near unexpected token `disable-fast-install' LT_INIT(disable-fast-install) 执行如下的步骤 您需要重新安装它才能修复错误,因此请按照以下步骤操作: 1] 如果已安装,请删除当前的库工具:sudo yum remove libtool 2]从官方网站下载 https://www.gnu.org/software/libtool/ 3] 解压缩它:tar -xzvf "name of the tar_file" 4] 输入文件夹和类型:./configure && make 5] 安装它:sudo make install 你完成了,错误应该被修复!如果不行的话,再把libtool安装回来 记得重新去运行./autogen.sh && ./configure,之前通过了./autogen.sh然后运行./configure就会一直这样报错,只有重新运行了./autogen.sh,才能运行./configure ``` PS:根据官方的安装,他的动态库下载保存到 ``` /usr/local/lib ``` 如果链接出现找不到动态库的情况,把动态库拷贝到系统默认的路径下,记住软连接也需要一起进行拷贝 ``` /usr/lib64 ``` ctemplate库渲染原理如下 ![image-20230718220848566](image/image-20230718220848566.png) 我们首先使用数据字典将网页中可能会进行需要进行渲染的变量进行保存,并给出变量渲染后的值是多少,然后在进行网页编写的时候,需要被渲染的变量就是用{{}}括起来,代码运行起来的时候,会自动进行变量的渲染 #### oj_view模块的描述 ``` 这个模块的作用是根据已有的html网页加上model模块的数据,然后进行对应的网页渲染,将渲染后的网页进行返回 all_questions_expand_html: 根据model模块获取的所有题目,然后根据预设好的html网页,将所有的题目进行渲染,然后将渲染好的网页进行返回 one_question_expand_html: 根据model模块获取的某一道题目,然后根据预设好的html网页,将所有的题目进行渲染,然后将渲染好的网页进行返回 ``` ### oj_control编写:核心的逻辑控制模块 ``` 这个模块我们分为三个类来写 首先是machine类,这个类用来保存给oj_server提供服务的主机他们的ip,port还有负载情况,在这个类中有一个mutex*,用来保护负载情况的变化,让它线程安全,为什么使用指针,是因为,以后我们会先创建出machine的对象,然后再给对象里面的成员变量进行复制,使用指针来解决mutex进制拷贝的问题 然后是load_balancing类,我们首先会有一个自己创建的配置文件,是在con目录里面的service_machines.conf文件,里面保存着提供编译运行服务主机的IP和端口,在构建这个类的对象时,在构造函数里面完成配置文件的读取,然后根据信息创建出多个machine对象,每个machine对象对应一个提供编译服务的机器,我们默认这些机器都是在线的 然后就是这个类里面最重要的函数intelligent_selection 可以根据在线机器的负载情况来选择负载最低的机器 使用轮询的方法,将在线列表里面的机器都进行遍历,然后挑选出负载最低的机器 最后是control类,这个类将model模块还有view模块以及load_balancing模块进行了一起使用 提供可三个函数,其功能都是与oj_server接收到的http请求相对应的 get_all_questions,可以获取所有题目列表的网页 get_one_question:可以获取对应编号的题目的编写网页 judge:对用户提交的代码进行判断 通过in_json拿到用户提交的代码 然后重新拼接用户代码+测试用例代码,形成新的代码,并同时添加cpu和mem的限制,再度形成json串 然后通过负载均衡选择一台负载最低的一台机器,将json串通过http请求发送过去,如果选择的时候,发现没有在线的主机了,就会提示去维护主机上线 如果请求没有的到对应主机的响应,那么对面的主机可能已经离线了,那么就可以吧对应的主机加入到离线列表里面 如此反复地去请求主机,知道找到一台可以响应的主机 ``` ### oj_server编写:用户请求的服务路由功能 ``` 这个模块主要提供用户请求的服务器路由功能 一般而言用户主要有三种请求 1.获取所有的题目列表 2.用户要根据题目编号,获取题目的内容 3.用户提交代码,使用我们的判题能力 这些功能我们都使用oj_control模块进行了实现 所以我们只要接收到对应的请求,然后调用调用对应的函数,得到结果之后,响应回去即可 我们这个捕捉了SIGQUIT信号,只要接收到这个信号,我们就把所有位于离线列表的主机,全部转移到在线主机列表 ``` ### 使用postman进行oj_server和compile_server联合调试 我们在本地使用三台机器执行compile_server,一台机器执行oj_server ![image-20230718230424257](image/image-20230718230424257.png) 然后我们通过postman向oj_server发送第一个请求,请求的是judge/1,意思是对用户对于id为1的题目所写出的代码,进行判题 ![image-20230718230806903](image/image-20230718230806903.png) 可以看到我们选择一台负载最低的主机,并将他的信息打印到了屏幕上 ![image-20230718231751604](image/image-20230718231751604.png) 负载最低的主机,执行了代码的编译和运行工作 ![image-20230718230830843](image/image-20230718230830843.png) 我们先关闭一台机器,然后看看现在oj_server如何选择负载最低机器,可以看见oj_server请求之前的那一台主机已经离线了,所以它选择了另一台机器,并且将当前离线和在线的主机都会打印出来 ![image-20230718231706070](image/image-20230718231706070.png) 当我们所有提供编译功能的机器都离线的时候,oj_server应该如何面对呢?可以看到它会提醒我们所有的编译主机已经离线了,需要进行对应的维护 ![image-20230718231305083](image/image-20230718231305083.png) ## 6、前端页面设计 ### 首页界面设计 页面上面的边框形式参考leetcode首页而进行设计的,当我们鼠标划过对应的的边框时,边框就会变成绿色,提升用户的使用体验,对于上面边框所展示的功能我们只实现了题库这一项,点击就可以进入我们在线oj题库,同时下面的"点击开始编程"也可以进入在线oj题库 ![image-20230720120604441](image/image-20230720120604441.png) ### 题目列表界面设计 可以看见后端我们提前准备好的题目,就会显示在这个页面(这里的题目并没有按照编号进行排序,还未实现排序),当我们的鼠标滑动到标题的上面,题目也会变成蓝色,给用户一个提醒,然后我们就会跳转到题目的编写区域 ![image-20230720121110608](image/image-20230720121110608.png) ### 答题界面设计 左边就是我们具体的题目描述,设计了一个下拉框,可进行上下左右的滚动,针对于题目太多显示不下的情况,右边就是我们写代码的区域,当我们写好代码的时候,可以点击右下角的提交代码,提交代码的按钮同样的,鼠标放上去时,会颜色的变化,提示用户,可以进行代码的提交,然后服务端将代码进行编译运行,将结果显示到下面的信息框 ![image-20230720122956014](image/image-20230720122956014.png) ## 7、综合测试 我们启动一个oj_server和三个compile_server ![image-20230720135021765](image/image-20230720135021765.png) 打开浏览器,进入到题目列表(这个时候已经排好序了) ![image-20230720135204969](image/image-20230720135204969.png) 进入到做题的页面,我们随便写点代码,保证能够通过编译和运行,然后疯狂点击提交代码 ![image-20230720135107590](image/image-20230720135107590.png) 可以看到,我们oj_server负载均衡式的把请求分配到三台编译主机,每台编译主机都是处理两个(httplib默认设置的全连接个数是6个) ![image-20230720135450675](image/image-20230720135450675.png) 我们将一个compile服务终止掉,oj_server负载均衡式的把请求分配到两台编译主机,每台编译主机都是处理三个(httplib默认设置的全连接个数是6个) ![image-20230720135533254](image/image-20230720135533254.png) 我们将另一个compile服务也终止掉,oj_server负载均衡式的把请求分配到一台编译主机,每台编译主机都是处理六个(httplib默认设置的全连接个数是6个) ![image-20230720135551026](image/image-20230720135551026.png) 当我们把所有的compile服务都终止掉,那么再次对oj_server发送请求的时候,就会提示尽快去维护编译主机,此时所有的主机都已经到离线队列里面了 ![image-20230720135617255](image/image-20230720135617255.png) 当我们重启了所有的compile_server之后,对oj_server发送SIGQUIT信号(ctrl + \\),然后就会把处于离线队列里面的主机添加到在线列表里面 ![image-20230720135634460](image/image-20230720135634460.png) 可以看见服务又可以正常的进行请求了 ![image-20230720135652605](image/image-20230720135652605.png) ## 8、项目的发布和部署 ### 项目的发布 ``` 在根目录下编写的makefile 执行make可以一键生成所有的程序 执行make ouput可以将所有有关项目的文件和程序打包到一个output的文件夹中 执行make clean可以将生成的程序全部删除掉并且将output也删除掉 ``` ### 项目的部署 在output中的compile_server目录下执行如下的命令,进行compile_server服务的部署 ```cmd nohup ./compile_server + port > & ``` 在output中的oj_server目录下执行如下的命令,进行oj_server服务的部署 ```cmd nohup ./oj_server > ../../log.txt 2>&1 & ``` - `nohup`:表示不挂起(no hang up),这意味着即使用户退出终端,该命令也会继续在后台运行。 - `./http_server`:表示要运行的 HTTP 服务器的可执行文件路径。 - `> ../log.txt`:表示将标准输出重定向到上级目录名为 "log.txt" 的文件中。这意味着所有服务器输出的消息都将被写入该文件中。 - `2>&1`:表示将标准错误重定向到标准输出,这样错误消息也会被写入到 "log.txt" 文件中。 - `&`:表示将该命令放入后台运行,这样用户可以继续使用终端而不影响服务器的运行。