【Linux网络编程】第二十二弹---深入理解 I/O 多路转接之 epoll:系统调用、工作原理、代码演示及应用场景

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】【Linux网络编程】

目录

1、I/O 多路转接之 epoll

1.1、epoll 初识

1.2、epoll 的相关系统调用

1.2.1、epoll_create

1.2.2、epoll_ctl 

1.2.3、epoll_wait

1.3、epoll 工作原理 

1.3.1、理解数据到达主机

1.3.2、epoll原理

1.4、代码演示一(框架实现)

1.4.1、主函数

1.4.2、EpollServer类

1.4、代码演示二(通信实现)

1.4.1、Loop()

1.4.2、HandlerEvent()

1.5、epoll 的优点

1.6、epoll 工作方式 

1.7、对比 LT 和 ET

1.8、理解 ET 模式和非阻塞文件描述符

1.9、epoll 的使用场景


1、I/O 多路转接之 epoll

1.1、epoll 初识

  • 按照 man 手册的说法: 是为处理大批量句柄而作了改进的 poll.
  • 它是在 2.5.44 内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44)
  • 它几乎具备了之前所说的一切优点,被公认为 Linux2.6 下性能最好的多路 I/O 就绪通知方法.

作用:为了等待多个fd,等待fd上面的新事件就绪,通知程序员,事件已经就绪,可以进行IO拷贝了! 

定位:只负责进行等,等就绪事件派发!

1.2、epoll 的相关系统调用

epoll 有 3 个相关的系统调用.

1.2.1、epoll_create

epoll_create()

epoll_create - 创建一个 epoll 的句柄.#include <sys/epoll.h>int epoll_create(int size);

参数: 

  • size :在早期版本的 Linux 中指定了监听的文件描述符数量上限,自从 linux2.6.8 之后,这个参数被忽略,因为 epoll 自动调整以处理最大数量的文件描述符。因此,传递任何大于 0 的值都是可以的,通常使用 1 作为默认值。

返回值:

  • 成功时,epoll_create() 返回一个非负的文件描述符,该描述符用于后续的 epoll 操作,如 epoll_ctl() 和 epoll_wait()
  • 失败时,返回 -1 并设置 errno 以指示错误类型。

注意:用完之后, 必须调用 close()关闭.

1.2.2、epoll_ctl 

epoll_ctl()

epoll_ctl - 允许你向一个 epoll 实例中添加、删除或修改监听的文件描述符及其相关的事件。#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数:

  • epfd由 epoll_create() 或 epoll_create1() 返回的 epoll 文件描述符
  • op要执行的操作,可以是以下三个值之一:
    • EPOLL_CTL_ADD:向 epoll 实例中添加一个新的文件描述符
    • EPOLL_CTL_DEL:从 epoll 实例中删除一个文件描述符
    • EPOLL_CTL_MOD修改一个已经存在于 epoll 实例中的文件描述符的监听事件
  • fd要添加、删除或修改的文件描述符
  • event指向一个 epoll_event 结构体的指针,该结构体指定了要监听的事件类型和数据。对于 EPOLL_CTL_DEL 操作,这个参数可以是 nullptr,因为删除操作不需要知道事件类型。

返回值:

  • 成功时,epoll_ctl() 返回 0
  • 失败时,返回 -1 并设置 errno 以指示错误类型。

struct epoll_event 结构如下: 

typedef union epoll_data 
{void    *ptr;int      fd;__uint32_t u32;__uint64_t u64;struct sockaddr sockaddr;    // 仅在特定情况下使用
} epoll_data_t;struct epoll_event 
{__uint32_t events;      // 事件类型,可以是多个事件的按位或组合epoll_data_t data;      // 与事件相关的用户数据,可以是文件描述符、指针或 sockaddr 结构
};

events 可以是以下几个宏的集合:

  • EPOLLIN : 表示对应的文件描述符可以读 (包括对端 SOCKET 正常关闭);
  • EPOLLOUT : 表示对应的文件描述符可以写;
  • EPOLLPRI : 表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来);
  • EPOLLERR : 表示对应的文件描述符发生错误;
  • EPOLLHUP : 表示对应的文件描述符被挂断;
  • EPOLLET : 将 EPOLL 设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的.
  • EPOLLONESHOT:只监听一次事件, 当监听完这次事件之后, 如果还需要继续监听这个 socket 的话, 需要再次把这个 socket 加入到 EPOLL 队列里. 

1.2.3、epoll_wait

epoll_wait()

epoll_wait - 阻塞调用线程,直到有至少一个文件描述符上的事件变得就绪,或者超时发生。#include <sys/epoll.h>int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

参数:

  • epfd由 epoll_create() 或 epoll_create1() 返回的 epoll 文件描述符
  • events指向一个 epoll_event 结构体数组的指针该数组用于存储返回的事件信息
  • maxeventsevents 数组的大小,即最多可以返回的事件数量
  • timeout等待事件的超时时间(毫秒)
    • 如果为 -1,则 epoll_wait() 将无限期地阻塞,直到有事件发生。
    • 如果为 0,则 epoll_wait() 将立即返回,即使没有任何事件发生(这可以用于非阻塞模式)。

返回值:

  • 成功时,epoll_wait() 返回就绪事件的数量,这些事件被存储在 events 数组中。
  • 失败时,返回 -1 并设置 errno 以指示错误类型。

1.3、epoll 工作原理 

1.3.1、理解数据到达主机

数据到达主机的原理涉及多个层级和协议的协同工作。通过逐层封装、转发和接收处理,数据能够准确地从源主机传输到目的主机。

硬件中断是由硬件设备发出的信号,用于通知计算机系统发生了某个事件,需要系统进行处理。这些硬件设备可以是磁盘、网卡、键盘、时钟等。 

1.3.2、epoll原理

1.4、代码演示一(框架实现)

1.4.1、主函数

老规矩,根据主函数反向实现类和成员函数!

// ./epoll_server 8888
int main(int argc, char *argv[])
{if(argc != 2){std::cerr << "Usage: " << argv[0] << " locak-port" << std::endl;exit(0);}uint16_t port = std::stoi(argv[1]);EnableScreen(); // 开启日志std::unique_ptr<EpollServer> svr = std::make_unique<EpollServer>(port);svr->InitServer();svr->Loop();return 0;
}

1.4.2、EpollServer类

EpollServer类的成员变量包括端口号,listen套接字,epfd(epoll_create()函数的返回值),接收事件的数组,成员函数与PollServer类基本一致

基本结构

class EpollServer
{const static int size = 128;const static int num = 128;public:EpollServer(uint16_t port);void InitServer();void Loop();~EpollServer();private:uint16_t _port;std::unique_ptr<Socket> _listensock;int _epfd;struct epoll_event revs[num];
};

构造析构函数

构造函数初始化端口号,根据端口号创建监听套接字对象以及创建epoll句柄析构函数关闭epfd(合法的前提下)和listenfd!

EpollServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>())
{_listensock->BuildListenSocket(port);_epfd = ::epoll_create(size);if (_epfd < 0){LOG(FATAL, "epoll_create error\n");exit(1);}LOG(INFO, "epoll create success,epfd: %d\n", _epfd); // 4
}~EpollServer()
{if (_epfd >= 0)::close(_epfd);_listensock->Close();
}

InitServer()

InitServer()函数使用系统调用(epoll_ctl)listensock添加到epoll!

void InitServer()
{// 新链接到来,我们认为是读事件就绪struct epoll_event ev;ev.events = EPOLLIN;ev.data.fd = _listensock->Sockfd(); //???// 必须先把listensock添加到epollint n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Sockfd(), &ev);if (n < 0){LOG(FATAL, "epoll_ctl error\n");exit(2);}LOG(INFO, "epoll_ctl success,add new sockfd: %d\n", _listensock->Sockfd());
}

Loop()

Loop()函数调用epoll_wait系统调用进行等待,根据返回值执行对应的操作:

1、返回值为0    :打印超时日志,并退出循环

2、返回值为-1   :打印出错日志,并退出循环

3、返回值大于0 :打印事件发生日志,暂时先退出循环

void Loop()
{int timeout = 1000;while (true){// _listensock->Accepter();int n = ::epoll_wait(_epfd, revs, num, timeout);switch (n){case 0:LOG(INFO, "epoll time out...\n");break;case -1:LOG(ERROR, "epoll error\n");break;default:LOG(INFO,"haved event happend!,n : %d\n",n);break;}}
}

运行结果

1.4、代码演示二(通信实现)

代码演示二只需要修改EpollServer类中主函数调用的成员函数即可

1.4.1、Loop()

Loop()函数调用epoll_wait系统调用进行等待,根据返回值执行对应的操作:

1、返回值为0    :打印超时日志,并退出循环

2、返回值为-1   :打印出错日志,并退出循环

3、返回值大于0 :打印事件发生日志,并处理合法事件

void Loop()
{int timeout = 1000;while (true){// 事件通知,事件派发// _listensock->Accepter();int n = ::epoll_wait(_epfd, revs, num, timeout);switch (n){case 0:LOG(INFO, "epoll time out...\n");break;case -1:LOG(ERROR, "epoll error\n");break;default:LOG(INFO, "haved event happend!,n : %d\n", n);HandlerEvent(n);break;}}
}

1.4.2、HandlerEvent()

HandlerEvent()函数处理就绪事件主要分为以下两步:

  • 1、从事件数组中读取合法fd和events
  • 2、判断读事件是否就绪
    • 2.1、listensock就绪
    • 2.2、normal sockfd就绪
void HandlerEvent(int n)
{for (int i = 0; i < n; i++){// 1.从事件数组中读取合法fd和eventsint fd = revs[i].data.fd;uint32_t revents = revs[i].events;LOG(INFO, "%d 上面有事件就绪了,具体事件是: %s\n", fd, EventsToString(revents).c_str());// 2.判断读事件是否就绪if (revents & EPOLLIN){// listensock 读事件就绪,新链接到来了if (fd == _listensock->Sockfd())Accepter();elseHandlerIO(fd);}}
}

Accepter()

Accepter()函数处理新链接主要分为以下两步:

  • 1、获取链接
  • 2、获取链接成功将新的 fd 和 读事件添加到epoll中(使用epoll_ctl系统调用)
void Accepter()
{InetAddr addr;int sockfd = _listensock->Accepter(&addr); // 肯定不会出错if (sockfd < 0){LOG(ERROR, "获取链接失败\n");return;}LOG(INFO, "得到一个新的链接: %d, 客户端信息: %s:%d\n", sockfd, addr.Ip().c_str(), addr.Port());// 得到了一个新的sockfd,我们能不呢个进行read,recv? 不能,不清楚有没有数据,没数据会阻塞// 等底层有数据(读事件就绪),read/recv才不会被阻塞// 底层有数据,谁最清楚?epoll// 将新的sockfd添加到epoll中!怎么做呢?struct epoll_event ev;ev.data.fd = sockfd;ev.events = EPOLLIN;::epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);LOG(INFO, "epoll_ctl success,add new sockfd: %d\n", sockfd);
}

HandlerIO()

HandlerIO()函数处理普通fd情况直接读取文件描述符中的数据根据recv()函数的返回值做出不一样的决策,主要分为以下三种情况:

1、返回值大于0,读取文件描述符中的数据,并使用send()函数做出回应!

2、返回值等于0,读到文件结尾,打印客户端退出的日志,将epfd从epoll中移除并关闭fd!

3、返回值小于0,读取文件错误,打印接受失败的日志,然后同上!

void HandlerIO(int fd)
{char buffer[4096];int n = ::recv(fd, buffer, sizeof(buffer) - 1, 0); // 会阻塞?不会if (n > 0){buffer[n] = 0;std::cout << buffer;std::string response = "HTTP/1.0 200 OK\r\n";std::string content = "<html><body><h1>hello linux,hello world</h1></body></html>";response += "Content-Type: text/html\r\n";response += "Content-Length: " + std::to_string(content.size()) + "\r\n";response += "\r\n";response += content;::send(fd, response.c_str(), response.size(), 0);}else if (n == 0){LOG(INFO, "client quit,close fd: %d\n", fd);// 1.从epoll中移除,从epoll中移除fd,这个必须是健康&&合法的fd,否则会移除出错::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr); // 进一步验证红黑树以fd作为键值// 2.关闭fd::close(fd);}else{LOG(ERROR, "recv error,close fd: %d\n", fd);// 1.从epoll中移除,从epoll中移除fd,这个必须是健康&&合法的fd,否则会移除出错::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);// 2.关闭fd::close(fd);}
}

运行结果

1.5、epoll 的优点

  • 接口使用方便: 虽然拆分成了三个函数, 但是反而使用起来更方便高效. 不需要每次循环都设置关注的文件描述符, 也做到了输入输出参数分离开
  • 数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中, 这个操作并不频繁(而 select/poll 都是每次循环都要进行拷贝)
  • 事件回调机制: 避免使用遍历, 而是使用回调函数的方式, 将就绪的文件描述符结构加入到就绪队列中, epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪. 这个操作时间复杂度 O(1). 即使文件描述符数目很多, 效率也不会受到影响.
  • 没有数量限制: 文件描述符数目无上限.

注意
网上有些博客说, epoll 中使用了内存映射机制

  • 内存映射机制: 内核直接将就绪队列通过 mmap 的方式映射到用户态. 避免了拷贝内存这样的额外性能开销.

这种说法是不准确的. 我们定义的 struct epoll_event 是我们在用户空间中分配好的内存. 势必还是需要将内核的数据拷贝到这个用户空间的内存中的.


请uu们对比总结 select, poll, epoll 之间的优点和缺点(重要, 面试中常见). 

1.6、epoll 工作方式 

你妈喊你吃饭的例子

你正在吃鸡, 眼看进入了决赛圈, 你妈饭做好了, 喊你吃饭的时候有两种方式:
1. 如果你妈喊你一次, 你没动, 那么你妈会继续喊你第二次, 第三次...(亲妈,水平触发)
2. 如果你妈喊你一次, 你没动, 你妈就不管你了(后妈, 边缘触发

epoll 有 2 种工作方式-水平触发(LT)边缘触发(ET)
假如有这样一个例子:

  • 我们已经把一个 tcp socket 添加到 epoll 描述符 
  • 这个时候 socket 的另一端被写入了 2KB 的数据
  • 调用 epoll_wait,并且它会返回. 说明它已经准备好读取操作
  • 然后调用 read, 只读取了 1KB 的数据
  • 继续调用 epoll_wait......

水平触发 Level Triggered 工作模式
epoll 默认状态下就是 LT 工作模式.

  • 当 epoll 检测到 socket 上事件就绪的时候, 可以不立刻进行处理. 或者只处理一部分.
  • 如上面的例子, 由于只读了 1K 数据, 缓冲区中还剩 1K 数据, 在第二次调用epoll_wait 时, epoll_wait 仍然会立刻返回并通知 socket 读事件就绪.
  • 直到缓冲区上所有的数据都被处理完, epoll_wait 才不会立刻返回.
  • 支持阻塞读写和非阻塞读写

边缘触发 Edge Triggered 工作模式
如果我们在第 1 步将 socket 添加到 epoll 描述符的时候使用了 EPOLLET 标志, epoll 进入 ET 工作模式.

  • 当 epoll 检测到 socket 上事件就绪时, 必须立刻处理.
  • 如上面的例子, 虽然只读了 1K 的数据, 缓冲区还剩 1K 的数据, 在第二次调用epoll_wait 的时候, epoll_wait 不会再返回了.
  • 也就是说, ET 模式下, 文件描述符上的事件就绪后, 只有一次处理机会.
  • ET 的性能比 LT 性能更高( epoll_wait 返回的次数少了很多). Nginx 默认采用ET 模式使用 epoll.
  • 只支持非阻塞的读写

select 和 poll 其实也是工作在 LT 模式下. epoll 既可以支持 LT, 也可以支持 ET. 

1.7、对比 LT 和 ET

  • 1、LT 是 epoll 的默认行为.
  • 2、使用 ET 能够减少 epoll 触发的次数. 但是代价就是强逼着程序猿一次响应就绪过程中就把所有的数据都处理完.
    • 相当于一个文件描述符就绪之后, 不会反复被提示就绪, 看起来就比 LT 更高效一些. 但是在 LT 情况下如果也能做到每次就绪的文件描述符都立刻处理, 不让这个就绪被重复提示的话, 其实性能也是一样的.
  • 3、另一方面, ET 的代码复杂程度更高了.
  • 4、ET的通知效率更高
  • 5、ET可能给对方一个更大的接受窗口,增加IO效率 -- 即ET的IO效率更高

1.8、理解 ET 模式和非阻塞文件描述符

使用 ET 模式的 epoll, 需要将文件描述设置为非阻塞. 这个不是接口上的要求, 而是 "工程实践" 上的要求,逻辑如下:

ET模式下,只通知一次,本轮数据没读完,epoll不在通知 -> ET模式一旦就绪,就必须把数据全部读完 -> 你怎么知道你把数据全部读完了? -> 循环读取,直到读取不到? -> 循环读取是不是可能出现阻塞问题? -> 因此需将fd设置为非阻塞!

有一个问题:LT也可以设置非阻塞,LT我也可以循环读取完毕啊,为什么要有ET呢?

最简单的理解,ET是被强制要求非阻塞的,但是LT可以是阻塞也可以是非阻塞!

假设这样的场景: 服务器接收到一个 10k 的请求, 会向客户端返回一个应答数据. 如果客户端收不到应答, 不会发送第二个 10k 请求. 

如果服务端写的代码是阻塞式的 read, 并且一次只 read 1k 数据的话(read 不能保证一次就把所有的数据都读出来, 参考 man 手册的说明, 可能被信号打断), 剩下的 9k 数据就会待在缓冲区中. 

此时由于 epoll 是 ET 模式, 并不会认为文件描述符读就绪. epoll_wait 就不会再次返回. 剩下的 9k 数据会一直在缓冲区中. 直到下一次客户端再给服务器写数据.epoll_wait 才能返回
但是问题来了.

  • 服务器只读到 1k 个数据, 要 10k 读完才会给客户端返回响应数据.
  • 客户端要读到服务器的响应, 才会发送下一个请求
  • 客户端发送了下一个请求, epoll_wait 才会返回, 才能去读缓冲区中剩余的数据. 

所以, 为了解决上述问题(阻塞 read 不一定能一下把完整的请求读完), 于是就可以使用非阻塞轮训的方式来读缓冲区, 保证一定能把完整的请求都读出来. 

如果是 LT 没这个问题. 只要缓冲区中的数据没读完, 就能够让 epoll_wait 返回文件描述符读就绪.

1.9、epoll 的使用场景

epoll 的高性能, 是有一定的特定场景的. 如果场景选择的不适宜, epoll 的性能可能适得其反.

  • 对于多连接, 且多连接中只有一部分连接比较活跃时, 比较适合使用 epoll.

例如, 典型的一个需要处理上万个客户端的服务器, 例如各种互联网 APP 的入口服务器,这样的服务器就很适合 epoll.

如果只是系统内部, 服务器和服务器之间进行通信, 只有少数的几个连接, 这种情况下用epoll 就并不合适. 具体要根据需求和场景特点来决定使用哪种 IO 模型. 

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

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

相关文章

双向列表的实现(C++)

一.实现思路 主要是一个空间存储一个数值&#xff0c;然后为了索引后面的数据单元和前面的数据单元&#xff0c;所以在每个空间里面还要存储前面和后面数据单元的指针&#xff0c;就形成了每个数据单元 后面就是要管理的是双向列表的头结点和尾节点&#xff0c;方便实现后面的头…

国产信创实践(国能磐石服务器操作系统CEOS +东方通TongHttpServer)

替换介绍&#xff1a; 国能磐石服务器操作系统CEOS 对标 Linux 服务器操作系统&#xff08;Ubuntu, CentOS&#xff09; 东方通TongHttpServer 对标 Nginx 负载均衡Web服务器 第一步&#xff1a; 服务器安装CEOS映像文件&#xff0c;可直接安装&#xff0c;本文采用使用VMware …

Linux——修改USB网卡设备节点名称

修改驱动&#xff1a; 测试&#xff1a; 参考资料&#xff1a; https://blog.csdn.net/ablexu2018/article/details/144868950

上手体验微软全新整合的王炸平台Fabric

体验确实不错&#xff0c;微软强大的生态能力。 把可视化&#xff0c;数仓&#xff0c;数据胡&#xff0c;数据工厂&#xff0c;机器学习&#xff0c;数据监控等技术都整合到一个平台了。所有数据全都存储在统一的one lake数据中心&#xff0c;消除数据孤岛问题。而且不同角色可…

浅析PCIe链路均衡技术原理与演进

在现代计算机硬件体系的持续演进中&#xff0c;PCIe技术始终扮演着核心角色&#xff0c;其作为连接 CPU 与各类周边设备的关键高速通信链路&#xff0c;不断推动着计算机性能边界的拓展。而 PCIe Link Equalization均衡技术&#xff0c;作为保障数据在高速传输过程中准确性与稳…

东京大学联合Adobe提出基于指令的图像编辑模型InstructMove,可通过观察视频中的动作来实现基于指令的图像编辑。

东京大学联合Adobe提出的InstructMove是一种基于指令的图像编辑模型&#xff0c;使用多模态 LLM 生成的指令对视频中的帧对进行训练。该模型擅长非刚性编辑&#xff0c;例如调整主体姿势、表情和改变视点&#xff0c;同时保持内容一致性。此外&#xff0c;该方法通过集成蒙版、…

Wireshark 学习笔记1

1.wireshark是什么 wireshark是一个可以进行数据包的捕获和分析的软件 2.基本使用过程 &#xff08;1&#xff09;选择合适的网卡 &#xff08;2&#xff09;开始捕获数据包 &#xff08;3&#xff09;过滤掉无用的数据包 &#xff08;4&#xff09;将捕获到的数据包保存为文件…

学习threejs,导入babylon格式的模型

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.BabylonLoader babyl…

Redis缓存穿透、缓存击穿、缓存雪崩

缓存穿透 定义&#xff1a; 查询一个不存在的数据&#xff0c;mysql查询不到数据也不会直接写入缓存&#xff0c;就会导致每次请求都查数据库 例子&#xff1a; 一个get请求&#xff1a;api/news/getById/-1 解决方案 方案一&#xff1a;缓存空数据 缓存空数据&#xff0c;查…

用Kimi做研究:准实验设计的智能解决方案

目录 1.研究策略设计 2.过程框架设计 3.背景变量 4.细节设计 准实验设计是一种介于实验与观察研究之间的研究方法&#xff0c;准实验设计是在无法完全控制实验条件的情况下进行因果关系的探索。与传统实验设计相比&#xff0c;准实验设计不具备随机分配实验对象到各处理组的…

RIS智能无线电反射面:原理、应用与MATLAB代码示例

一、引言 随着无线通信技术的快速发展,人们对通信系统的容量、覆盖范围、能效以及安全性等方面的要求日益提高。传统的无线通信系统主要通过增加基站数量、提高发射功率和优化天线阵列等方式来提升性能,但这些方法面临着资源有限、能耗高和成本上升等挑战。因此,探索新的无线…

解决nginx多层代理后应用部署后访问发现css、js、图片等样式加载失败

一般是采用前后端分离部署方式&#xff0c;被上一层ng代理后&#xff0c;通过域名访问报错&#xff0c;例如&#xff1a;sqx.com.cn/应用代理路径。 修改nginx配置&#xff0c;配置前端页面的路径&#xff1a; location / {proxy_pass http://前端页面所在服务器的IP:PORT;pro…

IoT平台在设备远程运维中的应用

IoT平台是物联网技术的核心组成部分&#xff0c;实现了设备、数据、应用之间的无缝连接与交互。通过提供统一的设备管理、数据处理、安全监控等功能&#xff0c;IoT平台为企业构建了智能化、可扩展的物联网生态系统。在设备远程运维领域&#xff0c;IoT平台发挥着至关重要的作用…

新时期下k8s 网络插件calico 安装

1、k8s master节点初始化完毕以后一直处于notreadey状态&#xff0c;一直怀疑是安装有问题或者是初始化有问题&#xff08;当然&#xff0c;如果真有问题要先解决这些问题&#xff09;&#xff0c;经过不断探索才发现是网络插件没有安装导致的&#xff0c;根据建议安装calico插…

LabVIEW 系统诊断

LabVIEW 系统诊断是指通过各种工具和方法检测、评估、分析和解决 LabVIEW 程序和硬件系统中可能存在的故障和性能问题。系统诊断不仅涵盖软件层面的调试与优化&#xff0c;还包括硬件交互、数据传输、实时性能等方面的检查和分析。一个成功的系统诊断能够显著提升LabVIEW应用程…

电脑之一键备份系统(One Click Backup System for Computer)

电脑之一键备份系统 相信使用电脑的的人都遇到过&#xff0c;电脑系统崩溃&#xff0c;开机蓝屏等原因&#xff0c;这个时候你急着用电脑办公&#xff0c;电脑却给你罢工是多么气人了&#xff0c;其实可以给电脑做一个系统备份。 最近每天都有系统蓝屏崩溃&#xff0c;这个实难…

课题推荐——基于GPS的无人机自主着陆系统设计

关于“基于GPS的无人机自主着陆系统设计”的详细展开&#xff0c;包括项目背景、具体内容、实施步骤和创新点。如需帮助&#xff0c;或有导航、定位滤波相关的代码定制需求&#xff0c;请点击文末卡片联系作者 文章目录 项目背景具体内容实施步骤相关例程MATLAB例程python例程 …

【小程序】5分钟快速入门抓包微信小程序

期末周无聊&#xff0c;抽点时间看看小程序渗透&#xff0c;先讲下微信小程序的抓包 工具&#xff1a;BurpsuiteProxifier step1 bp先开个端口代理&#xff0c;演示用的8080(懒得再导证书) step2 Proxifier设置好bp的代理 step3 随便启动个微信小程序&#xff0c;任务管理…

腾讯云AI代码助手-公司职位分析AI助手

作品简介 腾讯云AI代码助手是一款智能工具&#xff0c;专注于为公司提供职位分析服务。通过自然语言处理和机器学习技术&#xff0c;它能快速解析职位描述&#xff0c;提取关键信息&#xff0c;并提供数据驱动的洞察&#xff0c;帮助公司优化招聘流程和职位设计。 技术架构 …

网络基础1 http1.0 1.1 http/2的演进史

http1.0 1.1 http/2的演进史&#x1f60e; &#xff08;连接复用 队头阻塞 服务器推送 2进制分帧&#xff09; 概述 我们主要关注的是应用层 传输层 http协议发展历史 http的报文结构&#xff1a;起始行 Header Body http的典型特征 http存在的典型问题 Keep Alive机制 chun…