网络中的阻塞与非阻塞以及reactor模型

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云原生内存数据库 Tair,内存型 2GB
简介: 网络中的阻塞与非阻塞以及reactor模型

一、网络IO的职责

操作IO

IO的操作方式

  • 阻塞IO
  • 非阻塞IO

通过fcntl修改fd是阻塞还是非阻塞,之后所有使用这个fd的系统调用都会表现为非阻塞

阻塞与非阻塞IO的具体差别:

数据在未就绪的处理

阻塞IO在系统调用中的流程

read阻塞IO调用流程:先去检测内核缓冲区有无数据,如果没有数据则阻塞等待,当客户端给我们发送数据了,然后我们的网卡驱动往里面内核缓冲区填充数据,然后阻塞的系统调用才会把阻塞去掉,然后就拷贝,然后系统调用就返回

write阻塞IO调用流程:先去检测内核缓冲区是否可以写数据(是否是满的),如果是满的,则就阻塞,一直等到网络协议栈把内核缓冲区数据发送出去了之后他有剩余空间了才会停止阻塞,将数据写进内核缓冲区,返回实际写入内核空间的字节数

**accept阻塞IO调用流程:**检查全连接队列中是否有节点,如果没有则一直阻塞等待,如果有则取出返回分配的客户端fd

非阻塞IO在系统调用中的流程

不管内核缓冲区是否可写可读,都直接返回。在必要情况会设置errno

网络编程系统调用具备检测和操作的功能

accept
  • 检测:全连接是否还有未处理的连接信息
  • 操作:从全连接队列中取出连接去获取并生成客户的fd以及ip端口信息
read
  • 检测:内核缓冲区是或否有数据
  • 操作:将内核缓冲区数据拷贝到用户态缓冲区来
write
  • 检测:内核缓冲区是否可以写数据(满了吗)
  • 操作:将用户态缓冲区拷贝到内核态缓冲区

二、系统调用在调用非阻塞IO的具体处理

connect

  • 多次调用connect,其返回值和**设置的errno**可能是不一样的,当errnoEISC-ONN时,就告诉我们连接已经建立成功了,如果是第一次调用connect的时候会返回EINPROGRESS,告诉我们正在建立连接

accept

  • 如果全连接队列中为空(客户端没有与服务器进行连接),那么accept会返回-1,并且errno会设置为EWOULDBLOCK,告诉我们全连接队列为空

read

  • read调用查看errno
  • read = -1 && errno = EWOULDBLOCK:代表读缓冲区还没有数据
  • read = -1 && errno = EINTR:代表可以读的时候被中断了

write

  • write调用查看errno
  • 同上 不过是代表缓冲区满了

三、网络中连接断开的情况

主动关闭

  • shutdown关闭一端
  • close是关闭两个端(首先是回收资源,然后读写端相应的会关闭)
  • 客户端写端关联服务端读端服务端读端关联客户端写端
  • 所以客户端调用shutdown关闭读端时,服务端对应的写端就会关闭(需要自己去判断)

被动关闭

  • read 返回 0:告诉我们连接断开了,更准确的说是服务端的读端关闭了
  • write返回-1 && errno = EPIPE:告诉我们服务端的写端关闭了
  • 为什么要区分读端关闭还是写端关闭
  • 因为有的服务器框架需要支持半关闭状态,
  • 如果是读端关闭了,服务器还可以将这个连接未发送出去的数据调用write发送出去,对应四次回收中对端发送fin包之前的时间段。

四、分离检测与操作

accept亲自检测全连接队列中是否有节点,而是使用io多路复技术来检测io是否就绪,保证当执行到accept时一定是io就绪的状态

并且io多路复用可以同时检测多个io的就绪状态

epoll为例介绍io多路复用

建立连接

  • io检测接收连接的处理流程
  • socket
  • bind&listen
  • 监听读事件(epollin
  • epoll如何监听的!epoll会监听listenfd的读事件,当我们连接监听成功的时候,全连接队列上会多一个节点,这个节点就会发送一个信号EPOLLIN)来告诉epoll(也可以是select、poll),就会触发读事件,就说明接收连接的io已经就绪,于是后续就调用accept,而现在accept则只会进行操作而不会进行检测,因为io多路复用已经做好了检测
  • io检测主动连接的处理流程
  • 服务器作为客户端 连接一些服务比如mysql
  • 监听写事件(epollout
  • 服务器调用conncet发送SYN包,此时状态是EINPROGRESS,如何由io多路复用来检测这个连接是否建立成功,io多路复用会检测三次握手的最后一次握手是否发送,当三次握手的最后一次握手发出时会发送一个信号,告诉epoll写事件触发了(所以当epoll监听到写事件触发的时候就说明我们的连接建立成功了)
  • 下面是一个epoll负责connect的检测的小demo
// 设置sockfd非阻塞
connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
epollfd = epoll_create(EPOLL_SIZE);  // 10
event.events = EPOLLOUT;
event.data.fd = sockfd;
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event);
epoll_wait(epollfd, events, EPOLL_SIZE, -1);  // -1代表阻塞
if (events[0].events & EPOLLERR || events[0].events & EPOLLHUP) {
    printf("connect failed\n");
    exit(EXIT_FAILURE);
}
else if (events[0].events & EPOLLOUT) {
    printf("connect success\n");
    // 发送一条消息
    ret = write(sockfd, send_buf, strlen(send_buf));
    if (ret == -1) {
        perror("write");
        exit(EXIT_FAILURE);
    }
}

连接断开

通过判断event[i].events来对客户单断开连接进行检测

  • EPOLLRDHUP:表示服务器读端关闭
  • EPOLLHUP:表示服务器读写端都关闭

消息到达

  • 客户端fd触发EPOLLIN(检测),然后调用read(操作)

消息发送

  • 客户 端fd触发EPOLLOUT(检测),然后调用write/send(操作)

epoll详解

epoll_create会创建一个红黑树和一个双端队列

epoll_ctl是对红黑树增删改,同时会跟网卡驱动建立回调关系,响应事件触发时会调用回调函数,这个回调函数会将触发的事件拷贝到rdlist双向链表中;

调用epoll_wait将会把rdlist中就绪事件拷贝到用户态中(拷贝出来后会清空内核态就绪队列的事件)

介绍epoll_wait的四个参数接口

  • 参数
  • epoll文件描述符
  • 用户空间的数组,用来接收内核空间中就绪队列的东西
  • 预期要拷贝多少事件
  • 定时(以毫秒为单位)
  • epoll是一种同步的io,没有阻塞与非阻塞之说,但是可以通过timeout来控制函数的阻塞,
    设置为-1则会表现为阻塞(就绪队列没有事件就一直阻塞),
    设置为0会表现为非阻塞(就绪队列)
  • 返回
  • 返回实际取出来多少事件

五、引入Reactor

是将对io的操作转化为对事件的处理

什么是reactor?

一个服务器有很多io,我们将他们递交给epoll内核管理,一旦io上面有事件(可读或者可写),就触发事件(可读或者可写)对应的回调函数处理业务。每个fd都有一个对应的结构体,里面保存了一些必要信息(回调函数,独立的读写缓冲区)

为什么要有reactor

reactorepoll的基础上面增加了什么,有了哪些好处

  • epoll:他是对io的管理
  • reactor:是对事件的管理,不同事件对应于不同的回调函数
  • 由于sock_item封装,对未处理完的事件,放到一个独立(独立于其他的fd(客户端))的buffer里面
  • 好处?
  • 比如实现了 一个httpserver,我们的recv只能收1024,但是get请求有2000个字符,每次接收都是放在fd自己的buffer里面,不会被其他的fd的输入所影响

reactor为什么搭配非阻塞?

为什么io多路复用帮我们检测好了io就绪,为什么还要使用非阻塞io,考虑一下三种情况

  • 多线程环境
  • 多个线程使用同一epoll监听同一fd
    当这个fd上有io的时候,就绪队列的节点会向每一个线程的epoll通知这里有新的连接建立。
    如果使用阻塞的io,则当一个线程的epoll_wait将它取出来后其他线程的epoll_wait就会阻塞在那儿
    **惊群!:**有一个海王,有很多个女朋友,每一个都叫老婆,有一次他同时碰到了这些女朋友,他交了医生老婆。结果他们都答应了
  • 边缘触发情况下
  • 调用epollwait,在客户端fd发生io事件的时候取出rdlist中的对应节点,后续不管readbuf还有没有数据都不会自动的把该fd放入rdlist中(而lt是会在readbuf还有数据时自动的放入rdlist,再让epollwait触发)
    在边缘触发下,readbuf必须一次性将数据读空,不然会出现类似tcp的粘包,如果fd设置为阻塞,则readbuf在已经读空的情况下不会返回而是继续阻塞,所以要设置为非阻塞来判断read读完
  • select bug
  • 当某个socket接收缓冲区有新数据分节到达,然后select报告这个socket描述符可读,但随后,协议栈检查到这个新分节检验和错误后丢弃了这个分节,这时候调用read无数据可读,如果socket没有设置非阻塞,那么read就会阻塞当前线程

六、reactor在中间件的运用

1、redis

使用单reactor

  • 具体环境
  • kv、数据结构、内存数据库
    通过key来查valuevalue可以支持种数据结构,这些数据的操作都是在内存上操作
  • 命令处理是单线程的
  • redis为什么要使用单reactor
  • 只能采用单reactor,因为核心业务逻辑是单线程的。
  • 去操作数据结构的时候很简单,操作命令时间复杂度比较低
  • redis是怎么处理的reactor大致流程
  • 创建listenfd
  • bind listen
  • listenfd注册读时间
  • 读事件触发回调accept
  • clientfd注册读事件
  • 读事件触发
  • redis针对reactor做了哪些优化
  • 开启多线程,将读数据和解协议绑定在一起放在另一个线程,将组包和写数据放在另一个线程处理,中间的业务逻辑还是放在主线程操作

2、memcache

reactor

  • 针对reactor做了哪些优化

主线程中有一个专门用于接收连接的reactor,用多少个连接也就有多少个子reactor,来一个连接创建一个线程,线程与线程的交互是使用pipe来交流的

3、nginx

reactor

多进程,每个进程有自己的epfd,监听同一listenfd,用户层处理惊群是为了可以自定义负载均衡

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
12天前
|
算法 前端开发 数据挖掘
【类脑智能】脑网络通信模型分类及量化指标(附思维导图)
本文概述了脑网络通信模型的分类、算法原理及量化指标,介绍了扩散过程、路由协议和参数模型三种通信模型,并详细讨论了它们的性能指标、优缺点以及在脑网络研究中的应用,同时提供了思维导图以帮助理解这些概念。
13 3
【类脑智能】脑网络通信模型分类及量化指标(附思维导图)
|
1天前
|
Kubernetes 负载均衡 安全
在k8S中,网络模型概念是什么?
在k8S中,网络模型概念是什么?
|
13天前
|
机器学习/深度学习 前端开发 数据挖掘
基于Python Django的房价数据分析平台,包括大屏和后台数据管理,有线性、向量机、梯度提升树、bp神经网络等模型
本文介绍了一个基于Python Django框架开发的房价数据分析平台,该平台集成了多种机器学习模型,包括线性回归、SVM、GBDT和BP神经网络,用于房价预测和市场分析,同时提供了前端大屏展示和后台数据管理功能。
|
7天前
|
机器学习/深度学习 人工智能 PyTorch
AI智能体研发之路-模型篇(五):pytorch vs tensorflow框架DNN网络结构源码级对比
AI智能体研发之路-模型篇(五):pytorch vs tensorflow框架DNN网络结构源码级对比
20 1
|
7天前
|
消息中间件 网络协议 Java
你不得不了解的网络IO模型知识
该文章主要讲述了网络I/O模型的相关知识,包括不同的I/O模型以及它们的特点和应用场景。
你不得不了解的网络IO模型知识
|
12天前
|
网络协议 Java 关系型数据库
16 Java网络编程(计算机网络+网络模型OSI/TCP/IP+通信协议等)
16 Java网络编程(计算机网络+网络模型OSI/TCP/IP+通信协议等)
41 2
|
17天前
|
机器学习/深度学习 算法 网络架构
神经网络架构殊途同归?ICML 2024论文:模型不同,但学习内容相同
【8月更文挑战第3天】《神经语言模型的缩放定律》由OpenAI研究人员完成并在ICML 2024发表。研究揭示了模型性能与大小、数据集及计算资源间的幂律关系,表明增大任一资源均可预测地提升性能。此外,论文指出模型宽度与深度对性能影响较小,较大模型在更多数据上训练能更好泛化,且能高效利用计算资源。研究提供了训练策略建议,对于神经语言模型优化意义重大,但也存在局限性,需进一步探索。论文链接:[https://arxiv.org/abs/2001.08361]。
18 1
|
1天前
|
机器学习/深度学习 API 异构计算
7.1.3.2、使用飞桨实现基于LSTM的情感分析模型的网络定义
该文章详细介绍了如何使用飞桨框架实现基于LSTM的情感分析模型,包括网络定义、模型训练、评估和预测的完整流程,并提供了相应的代码实现。
11 0
|
3天前
|
缓存 算法 网络性能优化
解决网络延迟和阻塞,有它,不服都不行!
解决网络延迟和阻塞,有它,不服都不行!
|
7天前
|
人工智能 物联网 异构计算
AI智能体研发之路-模型篇(一):大模型训练框架LLaMA-Factory在国内网络环境下的安装、部署及使用
AI智能体研发之路-模型篇(一):大模型训练框架LLaMA-Factory在国内网络环境下的安装、部署及使用
31 0