websevere服务器从零搭建到上线(三)|IO多路复用小总结和服务器的基础框架

文章目录

  • epoll
    • select和poll的优缺点
    • epoll的原理以及优势
    • epoll
  • 好的网络服务器设计
  • Reactor模型
    • 图解Reactor
  • muduo库的Multiple Reactors模型

epoll

select和poll的优缺点

1、单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于 select采用轮询的方式扫描文件描述符,文件描述符数量越多,性能越差;(在linux内核头文件中,有 这样的定义:#define __FD_SETSIZE 1024

2、内核 / 用户空间内存拷贝问题,select需要复制大量的句柄数据结构,产生巨大的开销

3、select返回的是含有整个句柄的数组,应用程序需要遍历整个数组才能发现哪些句柄发生了事件

4、select的触发方式是水平触发,应用程序如果没有完成对一个已经就绪的文件描述符进行IO操作, 那么之后每次select调用还是会将这些文件描述符通知进程

相比select模型,poll使用链表保存文件描述符,因此没有了监视文件数量的限制,但其他三个缺点依然存在

以select模型为例,假设我们的服务器需要支持100万的并发连接,则在__FD_SETSIZE 为1024的情况 下,则我们至少需要开辟1k个进程才能实现100万的并发连接。除了进程间上下文切换的时间消耗外, 从内核/用户空间大量的句柄结构内存拷贝、数组轮询等,是系统难以承受的。因此,基于select模型的 服务器程序,要达到100万级别的并发访问,是一个很难完成的任务。

epoll的原理以及优势

epoll的实现机制与select/poll机制完全不同,它们的缺点在epoll上不复存在。

设想一下如下场景:有100万个客户端同时与一个服务器进程保持着TCP连接。而每一时刻,通常只有 几百上千个TCP连接是活跃的(事实上大部分场景都是这种情况)。如何实现这样的高并发?

  1. 在select/poll时代,服务器进程每次都把这100万个连接告诉操作系统(从用户态复制句柄数据结构到 内核态),让操作系统内核去查询这些套接字上是否有事件发生,轮询完成后,再将句柄数据复制到用 户态,让服务器应用程序轮询处理已发生的网络事件,这一过程资源消耗较大,因此,select/poll一般 只能处理几千的并发连接。
  2. epoll的设计和实现与select完全不同。epoll通过在Linux内核中申请一个简易的文件系统(文件系统一 般用什么数据结构实现?B+树,磁盘IO消耗低,效率很高)。把原先的select/poll调用分成以下3个部分:
  • 调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
  • 调用epoll_ctl向epoll对象中添加这100万个连接的套接字
  • 调用epoll_wait收集发生的事件的fd资源

如此一来,要实现上面说是的场景,只需要在进程启动时建立一个epoll对象,然后在需要的时候向这 个epoll对象中添加或者删除事件。同时,epoll_wait的效率也非常高,因为调用epoll_wait时,并没有 向操作系统复制这100万个连接的句柄数据,内核也不需要去遍历全部的连接。

//epoll_create在内核上创建的eventpoll结构如下:
struct eventpoll{
	.... 
	/*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/ 
	struct rb_root rbr; 
	/*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/ 
	struct list_head rdlist; 
	....
};

epoll

epoll重点掌握LT模式和ET模式。

关于这一节可以读以下两篇博客:
IO多路转接(复用)之epoll
epoll边沿模式的非阻塞方法

好的网络服务器设计

陈硕老师的原话
在这个多核时代,服务端网络编程如何选择线程模型呢? 赞同libev作者的观点:one loop per thread is usually a good model这样多线程服务端编程的问题就转换为如何设计一个高效且易于使 用的event loop,然后每个线程run一个event loop就行了(当然线程间的同步、互斥少不了,还有其 它的耗时事件需要起另外的线程来做)。

event loop 是 non-blocking 网络编程的核心,在现实生活中,non-blocking 几乎总是和 IOmultiplexing 一起使用,原因有两点:

  • 没有人真的会用轮询 (busy-pooling) 来检查某个 non-blocking IO 操作是否完成,这样太浪费 CPU资源了。
  • IO-multiplex 一般不能和 blocking IO 用在一起,因为 blocking IO 中 read()/write()/accept()/connect() 都有可能阻塞当前线程,这样线程就没办法处理其他 socket 上的 IO 事件了。

所以,当我们提到 non-blocking 的时候,实际上指的是 non-blocking + IO-multiplexing,单用其 中任何一个都没有办法很好的实现功能

更强大的网络服务器Nginx!
采用的是epoll + fork 而不是epoll+pthread!
用多个进程程来监听新链接,不想muduo只有一个线程来监听网络连接

强大的nginx服务器采用了epoll+fork模型作为网络模块的架构设计,实现了简单好用的负载算法,使 各个fork网络进程不会忙的越忙、闲的越闲,并且通过引入一把乐观锁解决了该模型导致的服务器惊群 现象,功能十分强大。

Reactor模型

Reactor模型是一个设计一个高性能网络服务器的常用模型。

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.
反应堆设计模式是一种事件处理模式,用于处理由一个或多个输入并发传递给服务处理程序的服务请求。然后,服务处理程序对传入的请求进行多路复用,并将它们同步地分派给相关的请求处理程序。

重要组件:Event事件、Reactor反应堆、Demultiplex IO多路复用事件分发器、Evanthandler事件处理器
之后我们主要关注这四个组件的通信即可。

图解Reactor

请添加图片描述

交互流程:

  1. 首先会把事件注册到反应堆上,所谓的注册指的是应用程序对该事件比较感兴趣,我们请求反应堆帮我来监听我所感兴趣的事件,并且在事件发生的时候调用我预置的回调函数Handler
  2. 反应堆可以理解为存储了一个Event事件以及事件处理的集合,我们的事件处理可以添加很多的选项,比如事件响应、事件处理等等。每一个Event都对应一个Handler,所以的反应堆就维护了这样一个集合。然后reactor会调用epoll_ctl来设置相关的方法来处理sockfd,这个过程是借助Demultiplex实现。
  3. 我们这里的Demultiplex用来处理epoll_ctl的相关处理,然后Reactor自己启动反应堆,反应堆的后端就能驱动事件分发器Demultiplex(其实就是开启epoll_wait)的使用,整个服务器呈现出阻塞的状态来等待新用户的链接或者是已连接用户的读写事件,epoll_wait监听到了新事件产生,Demultiplex会把事件给反应堆返回。

为什么Demultiplex会返回给Reactor呢,因为事件Event发生后,我们需要调用对应的事件处理器Handler,这是我们注册在Reactor中的

  1. 最后对于发生事件的Event,我们就通过一个Map表来找到该事件Event对应的那个EventHandler,最后处理该任务。

muduo库的Multiple Reactors模型

在这里插入图片描述

在muduo网络库中,Reactor中已经集成了Demultiplex IO多路复用事件分发器组件(图片来源见水印)

在 muduo 库中,许多 client 在 MainReactor 中得到了连接请求的响应,并与 WebServer 建⽴具体的连接。然后通过⼀个叫 Acceptor 的模块,将具体的连接分配给到⼀些叫做 SubReactor 的 模块,在 SubReactor 中对具体的连接进⾏读、编码、计算、解码和写操作(即对 client 请求的响应)。

所以改图有一点不准确,Reactor其实就是存储了事件以及事件处理器,仅此而已,所以上图应该画成事件份发器。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是X电容和Y电容?

先补充个知识: 一、什么是差模信号和共模信号 差模信号:大小相等,方向相反的交流信号;双端输入时,两个信号的相位相差180度 共模信号:大小相等。方向相同。双端输入时,两个信号相同。 二、安规…

小程序如何重启

用户在使用小程序的过程中,有时候会碰到一些问题。比如小程序数据不加载、卡顿、崩溃或者出现其他异常情况。这时候,最简单的办法就是重启小程序。但是很多客户不知道如何重启小程序,下面就具体介绍小程序重新启动的几种方法。 1. 强制关闭&…

CWDM、DWDM、MWDM、LWDM:快速了解光波复用技术

在现代光纤通信领域,波分复用(WDM)技术作为一项先进的创新脱颖而出。它通过将多个不同波长和速率的光信号汇聚到一根光纤中来有效地传输数据。本文将深入探讨几种关键的 WDM 技术(CWDM、DWDM、MWDM 和 LWDM)&#xff0…

流量分析。

流量分析 在Wireshak抓包可以看到正常的执行流程如下: ● Client向Server发起Load data local infile请求 ● Server返回需要读取的文件路径 ● Client读取文件内容并发送给Server ● PS:在本机上启动服务端与客户端,启动wireshark 抓包&…

根据相同的key 取出数组中最后一个值

数组中有很多对象 , 需根据当前页面的值current 和 数组中的key对比 拿到返回值 数据结构如下 之前写法 const clickedItem routeList.find(item > item.key current) // current是当前页 用reduce遍历数组返回最后一个值 const clickedItem routeList.reduce((lastIte…

41.乐理基础-拍号-小节、小节线、终止线

小节线:下图红框中的竖线就是小节线 小节、终止线:最后的终止线就是文字意思表示乐谱结束了,后面没有了 下图中 0.5表示0.5拍(八分音符)、1表示1拍(四分音符)、0.25表示0.25拍(十六分…

学习Rust的第29天: cat in Rust

今天即将是这个系列的最后一次内容,我们正在catRust 中从 GNU 核心实用程序进行重建。cat用于将文件内容打印到STDOUT.听起来很容易构建,所以让我们开始吧。 GitHub 存储库:GitHub - shafinmurani/gnu-core-utils-rust 伪代码 function read(…

Transformer详解:从放弃到入门(一)

Transformer由论文《Attention is All You Need》提出,是一种用于自然语言处理(NLP)和其他序列到序列(sequence-to-sequence)任务的深度学习模型架构,在自然语言处理领域获得了巨大的成功,在这个…

免费开源线上线下交友社交圈子系统 小程序+APP+H5 可支持二开!

为什么要玩社交软件:互联网社交软件的独特优势 首先,社交软件为我们提供了一个便捷的沟通方式。在传统的交往方式中,人们需要面对面交流,这种方式在时间和空间上都受到限制。而社交软件打破了这些限制,无论我们身处何地…

如何复制本地docker镜像到其他主机

(1)打包镜像 比如我要复制的镜像是grafana的镜像 docker images 这里我把打包的镜像放在了根~目录下,如截图所示: docker save grafana/grafana:latest -o ~/grafana.jar (2)移动镜像 scp命令拷贝镜像到目标…

linux学习:线程池

目录 原理 初始线程池 运行中的线程池 相关结构体 api 线程池初始化 投送任务 增加活跃线程 删除活跃线程 销毁线程池 例子 thread_pool.h thread_pool.c test.c 测试程序 原理 一个进程中的线程就好比是一家公司里的员工,员工的数目应该根据公司的…

AI神助攻!小白也能制作自动重命名工具~

我们平时从网上下载一些文件,文件名很多都是一大串字母和数字,不打开看看,根本不知道里面是什么内容。 我想能不能做个工具,把我们一个文件夹下面的所有word、excel、ppt、pdf文件重命名为文件内容的第一行。 我们有些朋友可能不会…

刷代码随想录有感(57):二叉搜索树中的众数

题干&#xff1a; 代码&#xff1a; class Solution { public:unordered_map<int,int>map;void traversal(TreeNode* root){if(root NULL)return;traversal(root->left);map[root->val];traversal(root->right);}bool static cmp(const pair<int,int>&a…

[蓝桥杯2024]-PWN:ezheap解析(堆glibc2.31,glibc2.31下的double free)

查看保护 查看ida 大致就是只能创建0x60大小的堆块&#xff0c;并且uaf只能利用一次 完整exp&#xff1a; from pwn import* #context(log_leveldebug) pprocess(./ezheap2.31)def alloc(content):p.sendlineafter(b4.exit,b1)p.send(content) def free(index):p.sendlineaft…

C++:运算符重载(=/==)

赋值运算符&#xff08;&#xff09;重载 在C中&#xff0c;赋值运算符可以被重载&#xff0c;允许用户定义类对象的赋值行为。通过重载赋值运算符&#xff0c;可以自定义对象的赋值操作&#xff0c;以便适应特定的需求和语义。当我们定义一个自定义的类时&#xff0c;比如一个…

语音识别---节拍器

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

商城数据库88张表结构完整示意图41~50(十二)

四十一&#xff1a; 四十二&#xff1a; 四十三&#xff1a; 四十四&#xff1a; 四十五&#xff1a; 四十六&#xff1a; 四十七&#xff1a; 四十八&#xff1a; 四十九&#xff1a; 五十&#xff1a;

说说你对盒子模型的理解?

一、是什么 当对一个文档进行布局&#xff08;layout&#xff09;的时候&#xff0c;浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型&#xff08;CSS basic box model&#xff09;&#xff0c;将所有元素表示为一个个矩形的盒子&#xff08;box&#xff09; 一个盒子由四…

开源推荐榜【MalusAdmin基于 Vue3/TypeScript/NaiveUI 和 NET7 Sqlsugar 开发的后台管理框架】

简介 Malus是海棠的意思&#xff0c;顾名思义&#xff0c;海棠后台管理系统&#xff0c;读音与【马卢斯】相近&#xff0c;也可称作为马卢斯后台管理系统。 基于NET Core | NET7/8 & Sqlsugar | Vue3 | vite4 | TypeScript | NaiveUI 开发的前后端分离式权限管理系统,采用…

2024SCVN南方时尚之夜:童模冰雪之境惊艳亮相

在广州黄埔君澜酒店&#xff0c;璀璨的灯光下&#xff0c;一场主题为“雪山”、“童模”与“时尚”的盛宴于2024年5月1日至5月3日华丽上演。这场名为“2024SCVN南方时尚之夜&绽放冰雪之境”的活动&#xff0c;如同一颗璀璨的明珠&#xff0c;镶嵌在初夏的广州&#xff0c;熠…