当前位置: 首页 > news >正文

【计网】实现reactor反应堆模型 --- 框架搭建

在这里插入图片描述

没有一颗星,
会因为追求梦想而受伤,
当你真心渴望某样东西时,
整个宇宙都会来帮忙。
--- 保罗・戈埃罗 《牧羊少年奇幻之旅》---

实现Reactor反应堆模型

  • 1 前言
  • 2 框架搭建
  • 3 准备工作
  • 4 Reactor类的设计
  • 5 Connection连接接口
  • 6 回调方法

1 前言

到目前为止,我学习了计算机网络,了解了网络传输的过程,理解网络协议栈的层与层之间的关系。实践了使用TCP进行的网络编程,也了解了协议的编写,实际了http协议下的通信过程。

最近学习了五种IO模型,可以通过多路转接EPOLL提高读取效率。

那么现在是否可以将多路转接与网络结合,编写一个高效处理网络请求的反应堆模型Reactor。今天我们搭建基础的结构。

2 框架搭建

在这里插入图片描述
我们想要搭建的是这样的结构:

  1. 最底层是Reactor:负责事件派发,管理connection套接字连接。可以添加监听套接字与普通套接字,其中都有对应的回调方法。可以通过套接字类型赋予连接对应的回调方法。通过多路转接IO获取就绪事件,找到对应connection执行事件。
  2. Connection连接:管理文件描述符的连接对象,内部有这个文件描述符的输入输出缓冲区,回调函数,客户端信息,就绪事件集。等待Reactor调用回调方法。
  3. Listener监听:这是专门管理监听套接字的对象,里面有对于监听套接字的方法,可以获取新连接。作为监听套接字connection的回调方法
  4. HandlerConnection普通套接字 :这是针对普通套接字的对象,里面有对于普通套接字事件就绪的处理方法类。

最底层的就是这三层结构。下面我们来实现这三层结构。

3 准备工作

在实现三层结构之前,我们先对多路转接IO进行封装,让代码尽可能解耦:

对于多路转接,我们设计一个基类,作为上层调用的统一接口。然后继承出子类Epoll poll select,在子类中分别实现对应的方法。

这里只提供了Epoll的封装:

  1. 构造函数:构造时创建EPOLL模型,获得EPOLLfd。
  2. AddEvent:添加事件,调用epoll_ctl_add方法即可。
  3. Wait:获取底层就绪事件,直接使用epoll_wait即可
#pragma once
#include <iostream>
#include <stdlib.h>
#include <sys/epoll.h>
#include "Log.hpp"
#include "Comm.hpp"using namespace log_ns;// 多路复用基类
class Mutliplex
{
public:Mutliplex(/* args */){}virtual bool AddEvent(int fd, uint32_t events) = 0;virtual int Wait(struct epoll_event revs[], int num, int timeout) = 0;~Mutliplex(){}
};// epoll poll select基类
class Epoller : public Mutliplex
{
private:static const int size = 128;public:Epoller(){_epollfd = ::epoll_create(size);if (_epollfd < 0){LOG(ERROR, "epoll create failed!\n");exit(EPOLL_CREATE);}}std::string EventToString(uint32_t revents){std::string ret;if (revents & EPOLLIN)ret += "EPOLLIN";if (revents & EPOLLOUT)ret += "| EPOLLOUT";return ret;}bool AddEvent(int fd, uint32_t events){struct epoll_event ev;ev.data.fd = fd;ev.events = events;int n = ::epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev);if (n < 0){LOG(ERROR, "epoll_ctl add failed , errno:%d", errno);return -1;}LOG(INFO, "epoll_ctl add fd:%d , events:%s\n", fd, EventToString(events).c_str());return true;}int Wait(struct epoll_event revs[], int num, int timeout){return ::epoll_wait(_epollfd, revs, num, timeout);}~Epoller(){}private:int _epollfd;
};

4 Reactor类的设计

之前的TcpServer等服务端都要在内部封装_listensock。如果封装了监听套接字那么代码结构就定型了,就必须要有对监听套接字的处理。而这里我们想将Reactor设计一个管理connection连接的类,不需要针对监听套接字进行特殊处理

成员变量:

  1. 通过fd映射Connection*对象的哈希表_conn
  2. 判断是否启动bool isrunning
  3. 构建一个Multipex对象 , 构造时建立epoll指针,负责处理多路转接IO
  4. 就绪事件组struct epoll_event revs[gnum]
  5. 针对监听套接字的方法集,在添加连接时可以将方法设置进入connection
  6. 针对普通套接字的方法集

回调方法的类型为using handler_t = std::function<void(Connection *conn)>;

#pragma once
#include <string>
#include <iostream>
#include <memory>
#include <unordered_map>#include "Connection.hpp"
#include "Epoller.hpp"using namespace log_ns;class Reactor
{
private:static const int gnum = 128;public:Reactor() : _epoller(std::make_unique<Epoller>()), _isrunning(false){}void SetOnNormal(handler_t OnRecver, handler_t OnSender, handler_t OnExcepeter){_OnRecver = OnRecver;_OnSender = OnSender;_OnExcepeter = OnExcepeter;}void SetOnConnect(handler_t OnConnect){_OnConnect = OnConnect;}// 加入连接void AddConnection(int fd, uint32_t events, const InetAddr &addr, int type){}void Dispatcher(){}~Reactor(){}private:// fd 映射连接表std::unordered_map<int, Connection *> _conn;// 是否启动bool _isrunning;std::unique_ptr<Mutliplex> _epoller;// 事件数组struct epoll_event revs[gnum];//_listen新连接到来handler_t _OnConnect;// 处理普通fd IOhandler_t _OnRecver;handler_t _OnSender;handler_t _OnExcepeter;
};
  • Addconnection接口 :首先通过 fd events 与客户端信息和连接类型建立connection , 进行设置对应的事件集, 然后根据type判断类型,设置connection的上层处理回调方法。注意这里要对 connReactor进行关联 !后续connection的模块进行讲解,设置addr方便打印日志(可以知道是哪一个客户端);然后通过fd events 托管给epoll 进行添加事件 。最后将连接放入哈希表中。
  • IsConnExists判断当前连接是否存在
  • Dispatch()事件派发接口:进行while循环,获取底层哪些事件就绪 储存在成员变量struct epoll_event revs[gnum],根据返回值 n 对n个事件进行处理!这里只处理 ERR HUP IN OUT 使用if语句ERR HUP直接设置为IN OUT后续统一处理IN事件就绪 事件派发 通过_conn[fd]找到对应连接 执行对应事件的回调函数(注意保证连接存在 且 回调方法存在)。

完整代码如下:

#pragma once
#include <string>
#include <iostream>
#include <memory>
#include <unordered_map>#include "Connection.hpp"
#include "Epoller.hpp"using namespace log_ns;class Reactor
{
private:static const int gnum = 128;public:Reactor() : _epoller(std::make_unique<Epoller>()), _isrunning(false){}void SetOnNormal(handler_t OnRecver, handler_t OnSender, handler_t OnExcepeter){_OnRecver = OnRecver;_OnSender = OnSender;_OnExcepeter = OnExcepeter;}void SetOnConnect(handler_t OnConnect){_OnConnect = OnConnect;}// 加入连接void AddConnection(int fd, uint32_t events, const InetAddr &addr, int type){// 1. 通过 fd  构建一个 connection指针 set对应的事件集Connection *conn = new Connection(fd);conn->SetReactor(this);conn->SetEvents(events);conn->SetConnectionType(type);conn->SetAddr(addr);// 2. TODO 设置对connection的上层处理 设置回调方法if (conn->Type() == ListenConnection){conn->RegisterHandler(_OnConnect, nullptr, nullptr); // 设置方法}else{conn->RegisterHandler(_OnRecver, _OnSender, _OnExcepeter); // 设置方法}// 3. fd 与 events 托管给epoll 添加事件 出错直接 return;int n = _epoller->AddEvent(fd, events);// 4. 托管给_connection_conn.insert(std::make_pair(fd, conn));// 添加连接成功}// 判断连接是否存在bool IsConnExist(int fd){return _conn.find(fd) != _conn.end();}void LoopOnce(int timeout){// 获取底层事件int n = _epoller->Wait(revs, gnum, -1);for (int i = 0; i < n; i++){// 文件描述符int fd = revs[i].data.fd;// 就绪事件uint32_t revents = revs[i].events;// 处理IN OUT ERR HUPif (revents & EPOLLERR)revents |= (EPOLLIN | EPOLLOUT);if (revents & EPOLLHUP)revents |= (EPOLLIN | EPOLLOUT);if (revents & EPOLLIN){// 调用回调方法if (IsConnExist(fd) && _conn[fd]->_handler_recver)_conn[fd]->_handler_recver(_conn[fd]);}if (revents & EPOLLOUT){// 调用回调方法if (IsConnExist(fd) && _conn[fd]->_handler_sender)_conn[fd]->_handler_sender(_conn[fd]);}}}void Dispatcher(){_isrunning = true;int timeout = -1;while (true){LoopOnce(timeout);PrintDebug();//打印托管的fd列表}_isrunning = false;}void PrintDebug(){std::string s = "已建立的连接:";for (auto &conn : _conn){s += std::to_string(conn.first) + ' ';}LOG(DEBUG, "epoll 管理的fd列表: %s\n", s.c_str());}~Reactor(){}private:// fd 映射连接表std::unordered_map<int, Connection *> _conn;// 是否启动bool _isrunning;std::unique_ptr<Mutliplex> _epoller;// 事件数组struct epoll_event revs[gnum];//_listen新连接到来handler_t _OnConnect;// 处理普通fd IOhandler_t _OnRecver;handler_t _OnSender;handler_t _OnExcepeter;
};

5 Connection连接接口

  1. 成员变量
    • 文件描述符fd
    • 需要关心的事件集 events
    • 输入缓冲区 输出缓冲区
    • 三种事件的回调方法
    • 设置一个Reactor* _R
  2. SetEvents接口:通过传入events 初始化 events
  3. Events接口返回事件集
  4. Sockfd返回对应fd
  5. RegisterHandler接口快速设置回调方法
  6. SetReactor(Reactor* R)接口 connection与Reactor进行绑定,执行自己属于的Reactor

对于这个Reactor* _R 指针,是监听套装字获取到连接时发挥作用。当监听套接字的事件就绪,在回调方法中可以通过参数Connection取出内部的_R指针,找到对应的Reactor,进行AddConnection操作

#pragma once#include <iostream>
#include <string>
#include <functional>#include "InetAddr.hpp"class Connection;
class Reactor;using handler_t = std::function<void(Connection *conn)>;#define ListenConnection 0
#define NormalConnection 1class Connection
{
public:Connection(int fd) : _sockfd(fd){}void RegisterHandler(handler_t recver, handler_t sender, handler_t excepter){_handler_recver = recver;     // 处理读取_handler_sender = sender;     // 处理写入_handler_excepter = excepter; // 处理异常}void SetEvents(uint32_t events){_events = events;}void SetAddr(const InetAddr &addr){_addr = addr;}int Sockfd(){return _sockfd;}uint32_t Events(){return _events;}int Type(){return _type;}void SetReactor(Reactor *R){_R = R;}void SetConnectionType(int type){_type = type;}Reactor *GetReactor(){return _R;}InetAddr GerInetAddr(){return _addr;}void AppendInbuffer(const std::string &in){_inbuffer += in;}std::string &Inbuffer(){return _inbuffer;}~Connection(){}private:int _sockfd;            // 套接字fduint32_t _events;       // 事件集std::string _inbuffer;  // 输入缓冲区std::string _outbuffer; // 输出缓冲区Reactor *_R;int _type;InetAddr _addr;public:handler_t _handler_recver;   // 处理读取handler_t _handler_sender;   // 处理写入handler_t _handler_excepter; // 处理异常
};

6 回调方法

这里需要两种回调方法类,一种针对监听套接字,一种针对普通套接字。

Listener
  1. Listener统一管理Tcp连接模块,管理_listensock
  2. 成员变量 :
    • std::unique_ptr _listensock Tcp套接字对象
    • int _port;端口号
  3. 通过端口号进行构造TcpSocket
  4. ListenSock接口返回_listensock的fd。
  5. Accepter(conn* , int* code)方法获取连接并得到文件描述符 (这里采用ET模式)首先将listensockfd 读取设置为非阻塞读取,然后进行while(true)进行非阻塞读取 ,根据Accepter返回的错误码通过code返回 通过错误码进行判断,当读取到一个新的fd时,通过conn的Reactor指针调用AddConnection 加入新连接!
#pragma once
#include <memory>
#include <iostream>
#include "Socket.hpp"
#include "Connection.hpp"using namespace log_ns;
using namespace socket_ns;// 处理listen套接字的读写
class Listener
{
public:Listener(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>(port)){_listensock->BuildListenSocket(_port);}int ListenSockfd(){return _listensock->GetSockfd();}void Accepter(Connection *conn){LOG(DEBUG, "%d socket ready\n", conn->Sockfd());// 非阻塞式读取while (true){errno = 0;int code = 0;InetAddr addr;int sockfd = _listensock->Accepter(&addr, &code);if (sockfd > 0){LOG(INFO, "成功获取连接, 客户端:%s sockfd:%d\n", addr.AddrStr().c_str(), sockfd);conn->GetReactor()->AddConnection(sockfd, EPOLLIN | EPOLLET, addr, NormalConnection);}else{if (code == EWOULDBLOCK){// 读取完毕LOG(INFO, "底层数据全部读取完毕!\n");break;}// 信号中断else if (code == EINTR){continue;}else{LOG(ERROR, "获取连接失败!\n");break;}}}}~Listener(){}private:uint16_t _port;std::unique_ptr<Socket> _listensock;
};
HandlerConnection
  1. 处理普通连接读写问题,这个的设计就比较简单了,注意其只复杂数据的读取,协议解析需要交给上层进行处理!
  2. HandlerRecver(conn*):我们先实现读取的逻辑!
  3. HandlerSender(conn*):后续实现
  4. HandlerExcepter(conn*):后续实现
#include <sys/types.h>
#include <sys/socket.h>
// 不应该让HandlerConnection处理报文
class HandlerConnection
{
private:const static int buffersize = 512;public:HandlerConnection(handler_t process) : _process(process){}void Recver(Connection *conn){// LOG(DEBUG , "client发送信息: %d\n" , conn->Sockfd());// 进行正常读写 --- 非阻塞读取while (true){char buffer[buffersize];int n = ::recv(conn->Sockfd(), buffer, sizeof(buffer) - 1, 0);if (n > 0){// buffer是一个数据块 添加到conn的输入缓冲区中buffer[n] = 0;conn->AppendInbuffer(buffer);// 数据交给上层处理}else if (n == 0){// 连接断开LOG(INFO, "客户端[%s]退出, 服务器准备关闭fd: %d\n", conn->GerInetAddr().AddrStr().c_str(), conn->Sockfd());conn->_handler_excepter(conn); // 统一执行异常处理}else{// 本轮数据读完了if (errno == EWOULDBLOCK){// 这是唯一出口break;}// 信号中断else if (errno == EINTR){continue;}// 出现异常else{conn->_handler_excepter(conn);return;}}}// 读取完毕,我们应该处理数据了!// 加入协议std::cout << "Inbuffer 内容:" << conn->Inbuffer() << std::endl;_process(conn);}void Sender(Connection *conn){}void Excepter(Connection *conn){}~HandlerConnection(){}private:handler_t _process;
};

至此,Reactor反应堆模型的框架已经搭建好了,下一篇文章我们将在这个的基础之上进行协议解析与数据处理!并设计如何将数据发回。这里只是简单的实现读取数据的逻辑!

相关文章:

【计网】实现reactor反应堆模型 --- 框架搭建

没有一颗星&#xff0c; 会因为追求梦想而受伤&#xff0c; 当你真心渴望某样东西时&#xff0c; 整个宇宙都会来帮忙。 --- 保罗・戈埃罗 《牧羊少年奇幻之旅》--- 实现Reactor反应堆模型 1 前言2 框架搭建3 准备工作4 Reactor类的设计5 Connection连接接口6 回调方法 1 …...

力扣中等难度热题——长度为K的子数组的能量值

目录 题目链接&#xff1a;3255. 长度为 K 的子数组的能量值 II - 力扣&#xff08;LeetCode&#xff09; 题目描述 示例 提示&#xff1a; 解法一&#xff1a;通过连续上升的长度判断 Java写法&#xff1a; C写法&#xff1a; 相比与Java写法的差别 运行时间 时间复杂…...

JSON格式

JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;易于人和机器阅读和解析。它基于JavaScript的对象表示法&#xff0c;但被广泛用于多种编程语言。 JSON中的数据类型 字符串&#xff08;String&#xff09;&#xff1a;用双引…...

O-RAN前传Spilt Option 7-2x

Spilt Option 7-2x 下行比特处理上行比特处理相关文章&#xff1a; Open Fronthaul wrt ORAN 联盟被称为下层拆分(LLS)&#xff0c;其目标是提高电信市场的灵活性和竞争力。下层拆分是指无线电单元(RU) 和分布式单元(DU) 之间的拆分。 O-RAN前传接口可以在 eCPRI 上传输。eCPR…...

【GeoJSON在线编辑平台】(2)吸附+删除+挖孔+扩展

前言 在上一篇的基础上继续开发&#xff0c;补充上吸附功能、删除矢量、挖孔功能。 实现 1. 吸附 参考官方案例&#xff1a;Snap Interaction 2. 删除 通过 removeFeature 直接移除选中的要素。 3. 挖孔 首先是引入 Turf.js &#xff0c;然后通过 mask 方法来实现挖孔的…...

确定图像的熵和各向异性 Halcon entropy_gray 解析

1、图像的熵 1.1 介绍 图像熵&#xff08;image entropy&#xff09;是图像“繁忙”程度的估计值&#xff0c;它表示为图像灰度级集合的比特平均数&#xff0c;单位比特/像素&#xff0c;也描述了图像信源的平均信息量。熵指的是体系的混乱程度&#xff0c;对于图像而言&#…...

大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…...

算法通关(3) -- kmp算法

KMP算法的原理 从题目引出 有两个字符串s1和s2,判断s1字符串是否包含s2字符串&#xff0c;如果包含返回s1包含s2的最左开头位置&#xff0c;不包含返回-1&#xff0c;如果是按照暴力的方法去匹配&#xff0c;以s1的每个字符作为开头&#xff0c;用s2的整体去匹配&#xff0c;…...

5G网卡network connection: disconnected

日志 5G流程中没有报任何错误&#xff0c;但是重新拿地址了&#xff0c;感觉像是驱动层连接断开了,dmesg中日志如下&#xff1a; [ 1526.558377] ippassthrough:set [ ip10.108.40.47 mask27 ip_net10.108.40.32 router10.108.40.33 dns221.12.1.227 221.12.33.227] br-lan […...

微积分复习笔记 Calculus Volume 1 - 4.9 Newton’s Method

4.9 Newton’s Method - Calculus Volume 1 | OpenStax...

Flutter自定义矩形进度条实现详解

在Flutter应用开发中&#xff0c;进度条是一个常见的UI组件&#xff0c;用于展示任务的完成进度。本文将详细介绍如何实现一个支持动画效果的自定义矩形进度条。 功能特点 支持圆角矩形外观平滑的动画过渡效果可自定义渐变色可配置边框宽度和颜色支持进度更新动画 实现原理 …...

如何设置 TORCH_CUDA_ARCH_LIST 环境变量以优化 PyTorch 性能

引言 在深度学习领域&#xff0c;PyTorch 是一个广泛使用的框架&#xff0c;它允许开发者高效地构建和训练模型。为了充分利用你的 GPU 硬件&#xff0c;正确设置 TORCH_CUDA_ARCH_LIST 环境变量至关重要。这个变量告诉 PyTorch 在构建过程中应该针对哪些 CUDA 架构版本进行优…...

CSS的三个重点

目录 1.盒模型 (Box Model)2.位置 (position)3.布局 (Layout)4.低代码中的这些概念 在学习CSS时&#xff0c;有三个概念需要重点理解&#xff0c;分别是盒模型、定位、布局 1.盒模型 (Box Model) 定义&#xff1a; CSS 盒模型是指每个 HTML 元素在页面上被视为一个矩形盒子。…...

【笔记】前后端互通中前端登录无响应

后来的前情提要 &#xff1a; 后端的ip地址在本地测试阶段应该设置为localhost 前端中写cors的配置 后端也要写cors的配置 且两者的url都要为localhost 前端写的baseUrl是指定对应的后端的ip地址以及端口号 很重要 在本地时后端的IP的地址也必须为本地的 F12的网页报错是&a…...

AI引领PPT创作:迈向“免费”时代的新篇章?

AI引领PPT创作&#xff1a;迈向“免费”时代的新篇章&#xff1f; 在信息爆炸的时代&#xff0c;演示文稿&#xff08;PPT&#xff09;作为传递信息和展示观点的重要工具&#xff0c;其制作效率和质量直接关系到演讲者的信息传递效果。随着人工智能&#xff08;AI&#xff09;…...

HTB:Perfection[WriteUP]

目录 连接至HTB服务器并启动靶机 1.What version of OpenSSH is running? 使用nmap对靶机TCP端口进行开放扫描 2.What programming language is the web application written in? 使用浏览器访问靶机80端口页面&#xff0c;并通过Wappalyzer查看页面脚本语言 3.Which e…...

鸿蒙next打包流程

目录 下载团结引擎 添加开源鸿蒙打包支持 打包报错 路径问题 安装DevEcoStudio 可以在DevEcoStudio进行打包hap和app 包结构 没法直接用previewer运行 真机运行和测试需要配置签名,DevEcoStudio可以自动配置, 模拟器安装hap提示报错 安装成功,但无法打开 团结1.3版本新增工具…...

uni-app 实现自定义底部导航

原博&#xff1a;https://juejin.cn/post/7365533404790341651 在开发微信小程序&#xff0c;通常会使用uniapp自带的tabBar实现底部图标和导航&#xff0c;但现实有少量应用使用uniapp自带的tabBar无法满足需求&#xff0c;这时需要自定义底部tabBar功能。 例如下图的需求&am…...

Vue前端开发:animate.css第三方动画库

在实际的项目开发中&#xff0c;如果自定义元素的动画&#xff0c;不仅效率低下&#xff0c;代码量大&#xff0c;而且还存在浏览器的兼容性问题&#xff0c;因此&#xff0c;可以借助一些优秀的第三动画库来协助完成动画的效果&#xff0c;如animate.css和gsap动画库&#xff…...

Java中的I/O模型——BIO、NIO、AIO

1. BIO&#xff08;Blocking I/O&#xff09; 1. 1 BIO&#xff08;Blocking I/O&#xff09;模型概述 BIO&#xff0c;即“阻塞I/O”&#xff08;Blocking I/O&#xff09;&#xff0c;是一种同步阻塞的I/O模式。它的主要特点是&#xff0c;当程序发起I/O请求&#xff08;比如…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序

一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...