项目:基于httplib/消息队列负载均衡式在线OJ
文章目录
- 写在前面
- 关于组件
- 开源仓库和项目上线
- 其他文档说明
- 项目亮点
- 使用技术和环境
- 项目宏观结构
- 模块实现
- 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中获取数据即可
使用技术和环境
本项目使用的技术主要有:
- C++STL
- Boost标准库
- cpp-httplib网络库
- JSON序列化反序列化库
- Redis
- 消息队列
开发环境:
- Ubuntu22.04云服务器
- vscode
项目宏观结构
对于项目来说,核心的部分其实就是三个模块:
- common:存储的是一些公共的功能
- compile_server:编译运行模块
- 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++11execlp("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_CPU
和RLIMIT_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; //转化成为KBsetrlimit(RLIMIT_AS, &mem_rlimit);
}
程序运行
接下来就是让程序进行运行了,程序运行主要有三种情况:
- 代码跑完,结果正确
- 代码跑完,结果不正确
- 代码没跑完,异常了
那在运行模块,我们只管新的是程序有没有正常跑完,只要跑完了就可以,至于结果正确还是不正确,并不是这里关心的重点,因此在这个模块中,其实也需要有标准输出和标准错误两个模块,这两个模块当中记录的是到底有没有正常结束的信息
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完成序列化和反序列化的内容
对于序列化和反序列化来说,要传递的参数其实比较简单:
输入
- code:用户提交的代码
- input:用户给代码做的输入
- cpu_limit:时间要求
- mem_limit:空间要求
输出
- status:状态码
- reason:请求结果
- stdout:运行结果
- 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: // 6desc = "内存超过范围";break;case SIGXCPU: // 24desc = "CPU使用超时";break;case SIGFPE: // 8desc = "浮点数溢出";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 stringstd::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,控制器控制的是核心业务逻辑
下面的步骤,是要搭建一个小型网站,具体步骤如下:
- 获取首页信息
- 编辑区域页面
- 提交判题功能
下面就基于这些内容,进行代码的编写
用户请求服务路由功能
这个部分内容相对简单,就是借助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. 获取被渲染的htmlctemplate::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. 获取被渲染的htmlctemplate::Template *tpl = ctemplate::Template::GetTemplate(src_html, ctemplate::DO_NOT_STRIP);//4. 开始完成渲染功能tpl->Expand(html, &root);}
};
Control模块
最后进入Control模块
对于Control模块来说,基本的调用逻辑为:
- 把in_json进行反序列化,得到题目id和源代码,input等
- 重新拼接用户代码和测试代码,可以得到一个新的代码
- 把新的代码给负载最低的主机,发起http请求把结果传输过去
- 把结果赋值给out_json即可
选择主机
主机如何选择呢?
在本项目中,提供的方法是使用轮询方式实现的负载均衡,对比所有的主机寻找一个负载最少的进行判断即可
由于要实现负载均衡,所以在进行实际的使用过程中,要对于负载均衡有体现,在实际的运行中,理应拥有几个服务器一起进行负载均衡,那对于这些服务器的管理是有必要的,所以就要先对提供服务的主机进行管理:
// 提供服务的主机
class Machine
{
public:string ip; //编译服务的ipint port; //编译服务的portuint64_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}
};
这样,整个项目的最基础的业务逻辑就使用结束了
相关文章:
项目:基于httplib/消息队列负载均衡式在线OJ
文章目录 写在前面关于组件开源仓库和项目上线其他文档说明项目亮点 使用技术和环境项目宏观结构模块实现compiler模块runner模块compile_run模块compile_server模块 基于MVC结构的OJ服务什么是MVC?用户请求服务路由功能Model模块view模块Control模块 写在前面 关于…...
详解python中的pandas.read_csv()函数
😎 作者介绍:我是程序员洲洲,一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主。 🤓 同时欢迎大家关注其他专栏,我将分享Web前后端开发、人工智能、机器学习、深…...
速盾:DDoS高防IP上设置转发规则
DDoS攻击是一种网络攻击方式,攻击者通过大量请求使目标服务器或网络资源超负荷运行,导致服务不可用。为了保护网络安全,减少DDoS攻击对网络的影响,使用DDoS高防IP可以是一种解决方案。而在DDoS高防IP上设置转发规则可以提高网络的…...
京东一面测开(KPI)
京东一面测开凉经(笔试ak) 3.8 面试官:你很优秀啊,你不用谦虚 没问技术相关,问了如何设计测试用例步骤一些理论: 什么是软件测试?其目的是什么? 软件测试有哪些类型?请列…...
Django框架中级
Django框架中级 – 潘登同学的WEB框架 文章目录 Django框架中级 -- 潘登同学的WEB框架 中间件自定义中间件常用中间件process_view() 使用中间件进行URL过滤 Django生命周期生命周期分析 Django日志日志配置filter过滤器自定义filter 日志格式化formatter Django信号内置信号定…...
cordova-plugin-inappbrowser内置浏览器插件
一、InAppBrowser(内置浏览器) 允许在在单独的窗口中加载网页。例如要向应用用户展示其他网页。当然可以很容易地在应用中加载网页内容并管理,但有时候需要不同的用户体验,InAppBrowser加载网页内容,应用用户可以更方便的直接返回到主应用。 二、安装命令: cordova pl…...
打造智慧工厂核心:ARMxy工业PC与Linux系统
智能制造正以前所未有的速度重塑全球工业格局,而位于这场革命核心的,正是那些能够精准响应复杂生产需求、高效驱动自动化流程的先进设备。钡铼技术ARMxy工业计算机,以其独特的设计哲学与卓越的技术性能,正成为众多现代化生产线背后…...
Java File IO
Java File IO ~主要介绍四个类 InputStream OutputStream FileReader FileWriter~ InputStream (字节流读取File) public static void main(String[] args) throws IOException {String filePath "D:\\Javaideaporject\\JavaBaseSolid8\\File\\t…...
MySQL 函数与约束
MySQL 函数与约束 文章目录 MySQL 函数与约束1 函数1.1 字符串函数1.2 数值函数1.3 日期函数1.4 流程函数 2 约束2.1 概述2.2 约束演示2.3 外键约束2.4 删除/更新行为 1 函数 函数是指一段可以直接被另一程序调用的程序或代码。 1.1 字符串函数 MySQL中内置了很多字符串函数&…...
12_1 Linux Yum进阶与DNS服务
12_1 Linux Yum进阶与DNS服务 文章目录 12_1 Linux Yum进阶与DNS服务[toc]1. Yum进阶1.1 自定义yum仓库1.2 网络Yum仓库 2. DNS服务2.1 为什么要使用DNS系统2.2 DNS服务器的功能2.3 DNS服务器分类2.4 DNS服务使用的软件及配置2.5 搭建DNS服务示例2.6 DNS特殊解析 1. Yum进阶 1…...
Spring Boot集成geodesy实现距离计算
1.什么是geodesy? 浩瀚的宇宙中,地球是我们赖以生存的家园。自古以来,人类一直对星球上的位置和彼此的距离着迷。无论是航海探险、贸易往来还是科学研究,精确计算两个地点之间的距离都是至关重要的。 Geodesy:大地测量…...
在Windows上用Llama Factory微调Llama 3的基本操作
这篇博客参考了一些文章,例如:教程:利用LLaMA_Factory微调llama3:8b大模型_llama3模型微调保存-CSDN博客 也可以参考Llama Factory的Readme:GitHub - hiyouga/LLaMA-Factory: Unify Efficient Fine-Tuning of 100 LLMsUnify Effi…...
01——生产监控平台——WPF
生产监控平台—— 一、介绍 VS2022 .net core(net6版本) 1、文件夹:MVVM /静态资源(图片、字体等) 、用户空间、资源字典等。 2、图片资源库: https://www.iconfont.cn/ ; 1.资源字典Dictionary 1、…...
33、matlab矩阵分解汇总:LU矩阵分解、Cholesky分解和QR分解
1、LU矩阵分解 语法 语法1:[L,U] lu(A) 将满矩阵或稀疏矩阵 A 分解为一个上三角矩阵 U 和一个经过置换的下三角矩阵 L,使得 A L*U。 语法2:[L,U,P] lu(A) 还返回一个置换矩阵 P,并满足 A P*L*U。 语法3:[L,U,P] …...
C语言——使用函数创建动态内存
一、堆和栈的区别 1)栈(Stack): 栈是一种自动分配和释放内存的数据结构,存储函数的参数值、局部变量的值等。栈的特点是后进先出,即最后进入的数据最先出来,类似于我们堆盘子一样。栈的大小和生命周期是由系统自动管理的,不需要程序员手动释放。2)堆(Heap): 堆是由…...
【PL理论】(16) 形式化语义:语义树 | <Φ, S> ⇒ M | 形式化语义 | 为什么需要形式化语义 | 事实:部分编程语言的设计者并不会形式化语义
💭 写在前面:本章我们将继续探讨形式化语义,讲解语义树,然后我们将讨论“为什么需要形式化语义”,以及讲述一个比较有趣的事实(大部分编程语言设计者其实并不会形式化语义的定义)。 目录 0x00…...
前端杂谈-警惕仅引入一行代码言论
插入一行 JavaScript 代码似乎是一种无受害者犯罪。这只是一个小脚本,对吧?但 JavaScript 可以导入更多 JavaScript。-杰里米基思 “这只是一行代码”是我们经常听到的宣传语。这也可能是我们对自己和他人说的最大的谎言。 “仅用一行添加样式”&#x…...
有关cookie配置的一点记录
Domain:可以用在什么域名下,按最小化原则设Path:可以用在什么路径下,按最小化原则Max-Age和Expires:过期时间,只保留必要时间Http-Only:设置为true,这个浏览器上的JS代码将无法使用这…...
Oracle如何定位硬解析高的语句?
查询subpool 情况 select KSMDSIDX supool,round(sum(KSMSSLEN)/1024/1024,2) SQLA_size_mb from x$ksmss where KSMDSIDX<>0 and KSMSSNAMSQLA group by KSMDSIDX;查询subpool top5 SELECT *FROM (SELECT KSMDSIDX subpool,KSMSSNAM name,ROUND(KSMSSLEN / 102…...
Linux卸载残留MySQL【带图文命令巨详细】
Linux卸载残留MySQL 1、检查残留mysql2、检查并删除残留mysql依赖3、检查是否自带mariadb库 1、检查残留mysql 如果残留mysql组件,使用命令 rpm -e --nodeps 残留组件名 按顺序进行移除操作 #检查系统是否残留过mysql rpm -qa | grep mysql2、检查并删除残留mysql…...
4句话学习-k8s节点是如何注册到k8s集群并且kubelet拿到k8s证书的
一、kubelet拿着CSR(签名请求)使用的是Bootstrap token 二、ControllerManager有一个组件叫CSRAppprovingController,专门来Watch有没有人来使用我这个api. 三、看到有人拿着Bootstrap token的CSR来签名请求了,CSRAppprovingContr…...
2024全国大学生数学建模竞赛优秀参考资料分享
0、竞赛资料 优秀的资料必不可少,优秀论文是学习的关键,视频学习也非常重要,如有需要请点击下方名片获取。 一、赛事介绍 全国大学生数学建模竞赛(以下简称竞赛)是中国工业与应用数学学会主办的面向全国大学生的群众性科技活动,旨…...
QPS,平均时延和并发数
我们当前有两个服务A和B,想要知道哪个服务的性能更好,该用什么指标来衡量呢? 1. 单次请求时延 一种最简单的方法就是使用同一请求体同时请求两个服务,性能越好的服务时延越短,即 R T 返回结果的时刻 − 发送请求的…...
【Python核心数据结构探秘】:元组与字典的完美协奏曲
文章目录 🚀一、元组⭐1. 元组查询的相关方法❤️2. 坑点🎬3. 修改元组 🌈二、集合⭐1. 集合踩坑❤️2. 集合特点💥无序性💥唯一性 ☔3. 集合(交,并,补)🎬4. …...
Golang | Leetcode Golang题解之第137题只出现一次的数字II
题目: 题解: func singleNumber(nums []int) int {a, b : 0, 0for _, num : range nums {b (b ^ num) &^ aa (a ^ num) &^ b}return b }...
Spring和SpringBoot的特点
1.Spring的特点 1.IOC和AOP是Spring的两大核心特性,即控制反转和依赖注入。 2.松耦合:IOC和AOP两大特性可以尽可能地将对象之间的关系解耦 3.可配置:提供外部化配置的方式,可以灵活地配置容器及容器中的Bean 4.一站式:…...
怎么使用join将数组转为逗号分隔的字符串
在JavaScript中,你可以使用Array.prototype.join()方法将一个数组转换为逗号分隔的字符串。join()方法接受一个可选的参数,该参数指定了数组元素之间的分隔符。如果不提供参数,则默认使用逗号(,)作为分隔符。 下面是一…...
Web前端博客论坛:构建、运营与用户体验的深度解析
Web前端博客论坛:构建、运营与用户体验的深度解析 在数字化浪潮的推动下,Web前端博客论坛成为了广大开发者交流技术、分享经验的重要平台。如何构建一个功能齐全、运营有序的博客论坛,以及如何提升用户体验,是摆在每一位前端开发…...
Java从入门到放弃
线程池的主要作用 线程池的设计主要是为了管理线程,为了让用户不需要再关系线程的创建和销毁,只需要使用线程池中的线程即可。 同时线程池的出现也为性能的提升做出了很多贡献: 降低了资源的消耗:不会频繁的创建、销毁线程&…...
基于51单片机的车辆动态称重系统设计
一 动态称重 所谓动态称重是指通过分析和测量车胎运动中的力,来计算该运动车辆的总重量、轴重、轮重和部分重量数据的过程。动态称重系统按经过车辆行驶的速度划分,可分为低速动态称重系统与高速动态称重系统。因为我国高速公路的限速最高是120,所以高速动态称重系统在理论…...
网站被降权怎么办/google play下载
环境:windows 10 10240 PHP环境apache2.2 php5.3.6 mysql-5.5.33-winx64在测试一个开源项目时,出现了ERROR:1366||Incorrect integer value: for column id at row 1错误在网上搜索解决办法:最终找到了办法。解释:在mysql 5.0以后…...
WordPress客户端/汕头自动seo
大家好,我是小匠。持续更新电脑配件成本报价。报价表内是行业在售的全新电脑配件。报价表内没有洋垃圾,也没有二手配件,根据列举的型号可放心购买,不会被坑!距离618还有14天,各大厂商都在调整价格备战&…...
如何把网站推广出去/做推广哪个平台效果好
问题描述 TortoiseSVN 下载的服务器地址发生变更,如何更改”checkout”远程地址 解决方案: 选中”checkout”文件夹,右键 > TortoiseSVN > relocate 弹出重新设置框 点击“确定”,变更远程地址成功...
react做的网站/成人教育培训机构
对应的译文叫做 "原文"。...
阿里云服务器ecs建站教程/竞价推广托管
我们知道,cookie是有有效期的,cookie的默认有效期是在从cookie生成后一直到浏览器关闭,我们可以设置cookie的有效期来指定它的失效期,用户也可以禁止cookie也可以手动的删除cookie。cookie是一小段信息,以键/值对的信息…...
自己电脑做采集网站/今日热搜榜排名最新
原文:WPF 跨应用程序域的 UI(Cross AppDomain UI)版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:htt…...