驾驭网络技术的未来:探索Reactor网络模型在当今应用领域的革新

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 本文介绍了Linux网络设计中的Reactor网络模型及其在实际应用中的重要性。Reactor模型是一种经典的事件驱动设计模式,广泛应用于构建高性能、可扩展的网络服务器。我们将探讨Reactor模型的基本原理和组成部分,并详细介绍了Reactors模型在Linux网络编程中的实现方式。首先解释了Reactor模型中的关键概念,如事件、事件处理器和事件循环,以及它们之间的交互过程。然后深入分析了Reactor模型的两种主要实现形式:单线程Reactor和多线程Reactor,并比较了它们的优缺点及适用场景。

一、reactor网络模型编程介绍

reactor是将对IO的检测转换为对事件的处理,是一种异步事件机制。reactor会使用IO多路复用进行IO检测,IO多路复用器一般是:select、poll、epoll。
reactor大致逻辑:
(1)socket()创建一个套接字,listenfd;
(2)bind()、listen()配置listenfd,绑定和监听;
(3)listenfd注册读事件,交由epoll管理
(4)读事件触发,回调accept
(5)客户端连接clientfd组成读事件
(6)相关事件调用相关回调函数

1.1、建立连接

接收客户端连接。

//...
int epfd=epoll_create(1);//创建epoll对象

//...
int listenfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字

//...
struct epoll_event ev;
ev.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev)//注册事件

//...
// 当触发listenfd的读事件,调用accept接收连接
struct sockaddr_in clientaddr;
socklen_t len=sizeof(clientaddr);
int clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,&len);

struct epoll_event ev;
ev.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ev)//注册新连接的读事件

//...

连接第三方服务。

//...
int epfd=epoll_create(1);//创建epoll对象

//...
int fd=socket(AF_INET,SOCK_STREAM,0);//创建套接字

//...
struct sockaddr_in clientaddr;
socklen_t len=sizeof(clientaddr);
connect(fd,(struct sockaddr *)&clientaddr,&len);//连接服务

//...
struct epoll_event ev;
ev.events=EPOLLOUT;
epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev)//注册事件

//...
// 当触发fd的写事件,连接建立成功
if(status==e_connecting && ev.events==EPOLLOUT)
{
   
   
    status=e_connected;
    epoll_ctl(epfd,EPOLL_CTL_DEL.fd,NULL);
}

//...

1.2、断开连接

//...
if(ev.events & EPOLLRDHUP)
{
   
   
    // 关闭服务器读端
    close_read(fd);
}
if(ev.events & EPOLLHUP)
{
   
   
    // 关闭服务器读写端
    close(fd);
}

//...

1.3、数据到达

// ...
if(ev.events & EPOLLIN)
{
   
   
    while(1)
    {
   
   
        int n=recv(clientfd,buffer,buffer_size,0);
        if(n<0)
        {
   
   
            if(errno==EINTR)
                continue;
            if(errno==EWOULDBLOCK)
                break;
            close(clientfd);
        }
        else if(n==0)
        {
   
   
            close_read();
        }
        else
        {
   
   
            // 处理业务
        }
    }
    // ...
}

// ...

1.4、数据发送

// ...
if(ev.events & EPOLLOUT)
{
   
   
    int n=send(clientfd,buffer,buffer_size,0);
    if(n<0)
    {
   
   
        if(errno==EINTR)
            continue;
        if(errno==EWOULDBLOACK)
        {
   
   
            struct epoll_event e;
            e.events=EPOLLOUT;
            epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&e)//注册事件
            return;
            // break;
        }
        close(clientfd);
    }
    else if(n==buffer_size)
    {
   
   
        epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,NULL);
        // epoll_ctl(epfd,EPOLL_CTL_MOD,clientfd,&e);
    }
    // ...
}

//...

1.5、reactor常见疑问

1、epoll惊群
何为”惊群“?网络编程经常使用多线程、多进程模型,每个线程或进程中都有一个epoll对象,通过socket()、bind()、listen()生成的listenfd可能会给多个epoll对象管理,当一个accept到来时所有的epoll都收到通知,所有进程或线程同时响应这一事件,然而最终只有一个accept成功。这就是”惊群“。
2、水平触发和边沿触发
水平触发:当读缓冲区中有数据时,一直触发,直到数据被读完。
边沿触发:来一次事件触发一次。读写操作一般需要配合循环才能全部读写完成。
3、reactor为什么要搭配非阻塞IO?
主要是三方面原因:
(1)多线程环境下,一个listenfd会被多个epoll(IO多路复用器)对象管理,当一个连接到来时所有的epoll都收到通知,所有的epoll都会去响应,但最终只有一个accept成功;如果使用阻塞,那么其他的epoll将一直被阻塞着。所以最好使用非阻塞IO及时返回。
(2)边沿触发下,事件触发才会读事件,那么需要在一次事件循环中把缓冲区读空;如果使用阻塞模式,那么当读缓冲区的数据被读完后,就会一直阻塞住无法返回。
(3)select bug。当某个socket接收缓冲区有新数据分节到达,然后select报告这个socket描述符可读,但随后,协议栈检查到这个新分节检验和错误,然后丢弃了这个分节,这时调用recv/read则无数据可读;如果socket没有设置成nonblocking,此recv/read将阻塞当前线程。
4、IO多路复用一定要搭配非阻塞IO吗?
不是,也可以使用阻塞模式。比如MySQL使用select接收连接,然后一个连接使用一个线程进行处理;也可以使用一个系统调用先获取读缓冲区的字节数,然后读一次就把数据读完,但是这样就导致效率比较低。

int n=EVBUFFER_MAX_READ_DEFAULT;
ioctl(fd,FIONREAD,&n);//获取读缓冲区的数据字节数

二、reactor应用场景

使用单个reactor的场景和使用多个reactor的场景。使用多个reactor又有多线程和多进程的不同用法。

2.1、redis——使用单reactor

redis是一种key-value结构、有丰富的数据结构、对内存进行操作的网络数据库组件。redis的命令处理是单线程的。

2.1.1、redis为什么使用单reactor?

要理解redis为什么只使用单个reactor,需要明白redis的命令处理是单线程的
redis提供丰富的数据结构,对这些数据结构进行加锁非常复杂,所以redis使用单线程进行处理;因为使用单线程进行命令处理,核心业务逻辑是单线程,那么使用再多的reactor是无法处理过来的;所以,redis使用单个reactor。

另外,redis操作具体命令的时间复杂度比较低,更加没有必要使用多个reactor。

2.1.2、redis处理reactor框图

image.png

2.1.3、redis对reactor的优化

对业务逻辑进行了优化,引入IO线程:
接收完数据后,将数据抛到IO线程进行处理;发送数据之前,将打包数据放在IO线程进行处理,再发送出去。参考上图,就是将(read+decode)放到线程中处理,将(encode+write)放在线程中处理。
原因:
对于单线程而言,当接收的数据或发送的数据过大时,会造成线程负载过大,需要引用多线程做IO数据处理。特别是解协议过程,数据庞大而且耗时,需要开一个IO线程进行处理。
场景例子:
客户端上传日志记录;客户端获取排行榜记录。

2.1.4、从reactor角度看redis源码

创建一个epoll对象:
image.png

创建套接字,绑定监听:
image.png

listenfd放到epoll管理:
image.png

监听事件:
image.png

处理事件:
image.png

为clientfd注册读事件:
mermaid-diagram-2023-07-20-225055.png

2.2、memcached——多线程方式使用多个reator

memcached是一种key-value结构、对内存进行操作的网络数据库组件。memcached的命令处理是多线程的。
memcached需要libevent,libevent就是一个事件驱动库,memcached对于网络上的使用都是基于libevent的。

2.2.1、memcached为什么使用多reactor?

memcached的key-value结构不像redis支持丰富的数据结构,它的value使用的数据结构相对简单,加锁也就相对容易。因此,可以引入多线程,提高效率。

2.2.2、memcached如何处理reactor?

memcached主线程会有一个reactor,主要负责接收连接;接收完连接后,经过负载均衡,通过pipe(管道)告诉子线程的reactor,将客户端的fd交由该线程的reactor管理;每个线程处理相对应的业务逻辑。
image.png

2.2.3、从reactor角度看memcached源码

github上下载最新的memcached
开始源码分析:

创建套接字,绑定监听:
image.png

注册listenfd读事件:

image.png

分配clientfd到具体的线程中,添加读事件:
image.png

2.3、nginx——多进程方式使用多个reator

nginx可以反向代理,利用多进程处理业务。
master会创建listenfd,并bind和listen;fork出多个进程,每个进程都有一个自己的epoll对象,listenfd交由多个epoll对象管理。这时会有惊群现象,需要处理;通过负载均衡处理事件。
image.png

2.3.1、解决"惊群"问题

加锁方式。nginx会开辟一个共享内存,把锁放在共享内存当中,多个进程去争夺这把锁,争夺到锁的才能进行接受连接。

2.3.2、负载均衡

定义一个进程最大的连接数,当连接数量超过总连接数量的7/8时,该进程就会暂停接受连接,将机会留个其他进程。
这样不会让一个进程拥有过多的连接,而其他进程连接数量过少;从而使每个进程的连接数量相对平衡。
当所有的进程接受连接的数量都达到总连接数量的7/8时,这是nginx接受连接将变得很缓慢。

总结

在本文中深入探讨了Linux Reactor网络模型,并着重介绍了其在实际应用中的重要性和优势。Reactors模型是一种高效的网络设计模式,它在处理并发连接时表现出色,使得我们能够构建高性能、可伸缩的网络应用程序。

首先,了解了Reactors模型的基本原理。它采用事件驱动的方式,通过一个主循环监听输入事件,一旦有事件发生,就会调用相应的处理程序。这种非阻塞的设计使得服务器能够高效地处理大量并发连接,而无需为每个连接创建一个线程。

接着,探讨了Reactors模型在Linux网络设计中的实际应用。还深入剖析了事件处理和回调机制,帮助读者理解如何优化网络应用的设计。

相比传统的多线程或多进程模型,Reactors模型能够更好地利用系统资源,减少上下文切换和线程创建的开销,从而提高应用的并发处理能力。

本文旨在帮助读者全面了解Linux Reactor网络模型,并鼓励他们在自己的网络应用中运用这一模型,以构建出更高性能、更可靠的网络应用。掌握Reactors模型的知识,将使读者能够更加自信地驾驭网络技术的未来,迎接不断变化的挑战。

image.png

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
11天前
|
Kubernetes 安全 Devops
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
37 10
有效抵御网络应用及API威胁,聊聊F5 BIG-IP Next Web应用防火墙
|
4天前
|
监控 安全 BI
什么是零信任模型?如何实施以保证网络安全?
随着数字化转型,网络边界不断变化,组织需采用新的安全方法。零信任基于“永不信任,永远验证”原则,强调无论内外部,任何用户、设备或网络都不可信任。该模型包括微分段、多因素身份验证、单点登录、最小特权原则、持续监控和审核用户活动、监控设备等核心准则,以实现强大的网络安全态势。
|
26天前
|
存储 监控 物联网
计算机网络的应用
计算机网络已深入现代生活的多个方面,包括通信与交流(电子邮件、即时通讯、社交媒体)、媒体与娱乐(在线媒体、在线游戏)、商务与经济(电子商务、远程办公)、教育与学习(在线教育平台)、物联网与智能家居、远程服务(远程医疗、智能交通系统)及数据存储与处理(云计算、数据共享与分析)。这些应用极大地方便了人们的生活,促进了社会的发展。
49 2
计算机网络的应用
|
19天前
|
存储 安全 网络安全
网络安全的盾与剑:漏洞防御与加密技术的实战应用
在数字化浪潮中,网络安全成为保护信息资产的重中之重。本文将深入探讨网络安全的两个关键领域——安全漏洞的防御策略和加密技术的应用,通过具体案例分析常见的安全威胁,并提供实用的防护措施。同时,我们将展示如何利用Python编程语言实现简单的加密算法,增强读者的安全意识和技术能力。文章旨在为非专业读者提供一扇了解网络安全复杂世界的窗口,以及为专业人士提供可立即投入使用的技术参考。
|
26天前
|
机器学习/深度学习 自然语言处理 语音技术
Python在深度学习领域的应用,重点讲解了神经网络的基础概念、基本结构、训练过程及优化技巧
本文介绍了Python在深度学习领域的应用,重点讲解了神经网络的基础概念、基本结构、训练过程及优化技巧,并通过TensorFlow和PyTorch等库展示了实现神经网络的具体示例,涵盖图像识别、语音识别等多个应用场景。
48 8
|
24天前
|
网络协议 物联网 数据处理
C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势
本文探讨了C语言在网络通信程序实现中的应用,介绍了网络通信的基本概念、C语言的特点及其在网络通信中的优势。文章详细讲解了使用C语言实现网络通信程序的基本步骤,包括TCP和UDP通信程序的实现,并讨论了关键技术、优化方法及未来发展趋势,旨在帮助读者掌握C语言在网络通信中的应用技巧。
35 2
|
25天前
|
机器学习/深度学习 人工智能 安全
探索人工智能在网络安全中的创新应用
探索人工智能在网络安全中的创新应用
|
6天前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
43 17
|
17天前
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。
|
18天前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
40 10