Linux定时器与时间轮 实现网络连接超时关闭

目录

原理理解

定时器超时触发可读事件机制

定时器

Linux定时器

Linux内核定时器API

时间轮

​编辑

使用方法

时间轮与基于事件驱动配合

回调函数与定时器

梳理定时器加入到Reactor服务器的整体逻辑

EventLoop模块如何将新连接放入时间轮管理

 新连接和定时器封装逻辑分析

连接超时的处理逻辑


原理理解

定时器超时触发可读事件机制

总框架梳理(EventLoop、epoll、Channel)

  • 总结:每一个EventLoop(Reactor)中,都利用epoll管理着一个连接Channel对象和一个定时器Channel对象。epoll如果监控到定时器对象有事件就绪(连接超时--设定的时间内连接没有响应),则交给时间轮对该定时器事件进行处理。如果连接对象的Channel响应,则交给线程池中的其他线程处理该事件。
  • 流程总结
    • 初始化
      • EventLoop初始化,同时创建一个epoll实例,用于管理所有的事件
      • 每个连接和定时器都被包装成一个Channel对象(各自包装成一个,也就是两个Channel对象),这些Channel对象被添加到epoll实例中进行监控
    • 新连接到来时
      • 新连接到来时,TcpServer创建一个新的连接Channel对象,并将其添加到EventLoop中
      • 同时设置一个定时器对象,将该定时对象放入到时间轮中进行管理时间轮又是由该Reactor中的epoll进行管理的
    • 事件监控
      • EventLoop进入事件循环,通过调用epoll_wait等待事件发生
      • 如果有事件发生,EventLoop则会获取所有被激活的Channel对象,也就是有连接发送可读事件的时候,会调用该连接预先设置的回调函数进行处理
    • 处理连接事件
      • 若连接Channel对象的文件描述符可读,则EventLoop会调用连接Channel的读事件回调函数
      • 其他事件类似
    • 处理定时事件
      • 如果定时器Channel对象的文件描述符可读,那么EventLoop会调用定时器Channel的读事件回调函数,此时就会触发时间轮去定时检查任务
      • 时间轮检查当前槽的所有任务,如果任务的延时时间已经到达(证明该连接超时了),则执行任务的回调函数,也就是在此时关闭超时连接

定时器超时后触发epoll,然后返回通知给进程进行处理

  • timefd定时器超时后,内核会将该定时器到期事件标记为文件描述符上的可读事件(定时器能够激活epoll的原因)
  • epoll检测到该事件已经就绪,通知进程定时器已经到期

定时器到期触发可读事件为什么必须读取8个字节

  • 定时器到期后,内核在该文件描述符关联的内核缓冲区,写入一个uint64_t(8字节)的整数
  • 整数表示的是自从上次读取后,定时器到期的次数
  • 同步与清空缓冲区:读取八个字节清空了内核缓冲区中保存的到期次数。这样做就可以确保下一次定时器到期的时候,可以正确记录新的到期次数。

定时器

Linux定时器

定时器与epoll多路复用机制结合

  • 触发可读事件
    • 定时器文件描述符变成可读的时候,I/O复用机制会检测到该文件描述符的可读事件,然后通知对应进程
    • 定时器按照设置的时间触发,当触发的时候,定时器变的可读,epoll_wait就检测到该事件可读,然后通知进程处理
    • 进程则通过从定时器文件描述符中读取的超时次数来进行处理对应事件
    • 需要将定时器创建的文件描述符加入到epoll实例中,然后监控其是否可读

Linux定时器的作用

  • 系统调度:操作系统使用定时器中断机制来切换任务,从而确保系统中的进程可以平等的获取CPU时间
  • 定时任务:进程可以使用定时器实现连接超时管理等
  • 网络协议超时管理
  • 资源管理

Linux定时器的实现方式

  • POSIX定时器(本文主要讨论,目的是为了实现网络连接的超时管理)
  • 内核定时器
  • 周期性定时器 

POSIX定时器功能总结

  • 精准:纳秒级别的定时精读,可以应用于对时间有严格要求的场景中
  • 异步通知:定时器到期后可以通过信号或者线程通知机制,异步通知应用程序进行处理
  • 多定时器支持:一个进程下可以创建多个定时器,可以应用在复杂的时间管理
  • 灵活:单词定时和周期性定时可以配合使用 

Linux内核定时器API

创建定时器

  • 参数
    • clockid指定定时器使用的时钟

      • CLOCK_REALTIME:系统实时时钟。
      • CLOCK_MONOTONIC:单调递增时钟,从系统启动开始计时,不受系统时间变化影响。
    • flags:指定定时器的行为,可以是以下一个或多个标志的组合:

      • TFD_NONBLOCK:非阻塞模式。
      • TFD_CLOEXEC:在执行exec()时关闭文件描述符
  • 返回值
    • 成功时,返回一个新的文件描述符(非负整数),表示创建的定时器。
    • 失败时,返回-1,并设置errno指示错误原因

 

设置指向定时器的文件描述符

  • fd:由timerfd_create返回的文件描述符,用于标识定时器。
  • flags:标志位,可以是以下值之一:
    • 0:相对时间模式,从当前时间开始计时。
    • TFD_TIMER_ABSTIME:绝对时间模式,从系统启动时的绝对时间开始计时。
  • new_value:指向itimerspec结构体的指针,指定定时器的初始到期时间和重复间隔。
  • old_value:指向itimerspec结构体的指针,用于返回定时器之前的设置(可以为NULL

 

 itimerspec结构体

  • 使用事例:每隔三秒就会检查一次可读事件
  memset(&new_value, 0, sizeof(new_value));new_value.it_value.tv_sec = 3;        // 5秒后第一次到期new_value.it_value.tv_nsec = 0;new_value.it_interval.tv_sec = 3;     // 之后每隔2秒到期一次new_value.it_interval.tv_nsec = 0;if (timerfd_settime(timer_fd, 0, &new_value, NULL) == -1) {perror("timerfd_settime");exit(EXIT_FAILURE);}

时间轮

时间轮是用来管理大量定时任务的数据结构。核心思想是将事件划分为固定长度的周期,在每个时间可读上存储对应时刻触发的定时任务。通过移动秒针来模拟时间流逝,同时触发可读上的定时任务。

时间轮实现机制分析

  • 初始化时间轮
    • 固定大小的数组来存储定时任务,每个位置表示一个时间槽
    • 数组的大小就是时间轮容量,也就是可以管理的最大时间延迟
  • 添加定时任务
    • 添加定时任务时,依据当前时间以及任务的的延迟时间计算出应该插入到那个槽
    • 每个任务槽下面又可以放置多个任务
  • 时间轮轮转
    • 时间轮每秒移动一次,也就是当前秒针向前移动一格
    • 每次秒针移动的时候,会检查当前槽中的所有任务,然后执行任务,同时将已经完成或者取消的任务从槽中移除
  • 任务执行和超时处理
    • 如果任务执行时间到达,则执行任务的回调函数
    • 如果任务被取消或者已经完成,则从槽中移除

时间轮时间复杂度与空间复杂度

  • 时间复杂度:O(1)
  • 空间复杂度:取决于任务的数量 

使用方法

时间轮管理任务逻辑分析

  • 添加定时器:新连接建立后,设置定时器,然后将其添加到时间轮中进行管理
  • 时间轮运转:时间轮每秒移动一次,然后检查当前槽中的任务,执行这些任务
  • 超时处理:如果任务超时,执行其回调函数,关闭该连接
  • 取消定时器:如果连接在超时之前完成所有操作,那么就取消定时器移除任务

时间轮与基于事件驱动配合

定时执行任务

  • 析构函数实现:将释放连接的操作,放入到析构函数中,那么当连接的时间到了之后,就可以自动销毁任务
  • 析构函数缺陷:需要单独的定义一个定时任务类,同时如果在预定的时间中间,突然要销毁的这个连接发消息了,那么还得需要更改它的销毁事件,得不偿失
  • 智能指针shared_ptr计数器实现:借助shared_ptr内部计数机制实现,只有当计数为0的时候,才会真正释放一个对象
    • 假设在销毁时间内,连接进行了通信,此时只需要向定时任务中添加任务类对象的shared_ptr
    • 所以当到达销毁时间的时候,此时该智能指针的计数是1,不会在析构中销毁

回调函数与定时器

定时任务与回调函数配合时,回调函数可以在定时器到时间后,自动执行特定代码,无需手动检查和触发。使用回调函数将定时器的逻辑与具体的任务逻辑分离。

详细参考“回调函数分析”

梳理定时器加入到Reactor服务器的整体逻辑

EventLoop模块如何将新连接放入时间轮管理

整体逻辑梳理

  • 接收新连接
    • TcpServer类主要负责监听新连接,构造函数中,该类会设置一个回调函数(新连接到达的时候使用),SetAcceptCallback()
    • 新连接到达后,TcpServer::NewConnection方法被调用,在该方法中,利用新连接类创建并且设置回调函数(ConnectedCallback)
  • 事件循环管理
    • 一个Reactor对应一个EventLoop类,该类负责管理关心的所有事件。每个连接和定时器都会被包装成一个Channel(注意是封装成两个Channel),然后这个Channel交给EventLoop监控这些事件。
    • EventLoop调用epoll系统调用,等待事件发生,在事件发生后,调用对应回调函数
  • 定时器管理
    • TimerWheel类负责管理所有定时器任务,通过内部循环管理这些定时器任务(一个定时器就对应一个连接),按照设定的时间去检查是否有任务需要执行
    • 新任务添加时,也就是新连接到来后,需要在特定的时间内取完成某项任务,此时就会调用TimerWheel::TimerAdd方法,该方法可以将任务添加到时间轮的适当位置。
  • 新连接放入时间轮逻辑梳理
    • 接收新连接后,通过TcpServerd中的回调函数TimerWheel::TimerAdd方法,将一个与新连接相关的定时任务,添加到时间轮中。
    • 新连接就爱建立后,设置一个定时器,目的是确保连接在一定时间内完成身份验证或者其他初始化操作
    • 新连接到来后,定时器和连接封装成两个Channel,然后这个连接的定时器会放入到时间轮中进行管理,如果特定时间内没有事件到来,时间轮会对其自动释放,如果有新连接到来,时间轮则会将这个定时器的位置往后放。

代码逻辑分析

  • TcpServer接收新连接后,将一个定时任务添加到时间轮中
  • EventLoop负责管理这些事件,并且定时检查事件轮中的任务是否需要执行

 新连接和定时器封装逻辑分析

新连接和定时器会各自封装成一个Channel,EventLoop监控

  • 连接事件封装:新连接会创建一个Channel对象进行封装,然后设置对应的回调函数。创建后的Channel对象交给EventLoop中进行监控
  • 定时器事件封装:定时器也是通过Channel对象进行封装,设置读事件的回调(定位定时器超时后,会自动触发可读事件,此时epoll就可以检测到),该事件封装好后也是交给EventLoop管理

总结Channel类、EventLoop类、Epoller类在服务器中的作用

  • Channel类:总体来说,该类就是事件的抽象封装,表示一个文件描述以及该事件发生的事件,其中还封装了对应事件的回调函数
  • EventLoop类:负责管理所有的Channel,同时通过epoll系统调用来等待事件发生
  • Epoller类:封装了epoll系统调用,用于监控所有事件

 

 

连接超时的处理逻辑

定时器事件和连接事件之间配合处理超时连接

  • 连接Channel对象
    • 新连接到来后,服务器创建一个新的Channel对象来管理该连接,这个Channel对象会关联到对应的文件描述符中(新连接的),然后设置读写回调函数
  • 定时器Channel对象
    • 服务器给新连接创建一个定时器,通过创建定时器的Channel对象,然后将定时器添加到时间轮中进行管理
    • 定时器的回调函数,则是一个负责在超时的时候才需要执行的任务,也就是关闭连接的任务逻辑
  • 定时器的Channel对象与连接Channel对象
    • 如果连接在定时器规定的时间内,完成了对应的操作,则服务器调用TimerWheel::TimerCancel方法取消该定时器
    • 如果定时器超时且连接没有完成所需要的操作,那么定时器的回调函数就会被触发,也就是最终会执行关闭连接的操作

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【系统架构设计师】十九、层次式架构设计理论与实践②

目录 四、数据访问层设计 4.1 数据访问模式 4.2 工厂模式在数据库访问层的应用 4.3 JavaBean中使用 JDBC 方式进行事务处理 4.4 连接对象管理设计 五、数据架构规划与设计 相关推荐 四、数据访问层设计 4.1 数据访问模式 数据访问模式有 5 种,分别是&#…

Ubuntu设置网络

进入网络配置文件夹 cd /etc/netplan 使用 vim 打开下的配置文件 打开后的配置 配置说明: network:# 网络配置部分ethernets:# 配置名为ens33的以太网接口ens33:addresses:# 为ens33接口分配IP地址192.168.220.30,子网掩码为24位- 192.168.220.30/24n…

爬虫 APP 逆向 ---> 粉笔考研

环境: 粉笔考研 v6.3.15:https://www.wandoujia.com/apps/1220941/history_v6031500雷电9 模拟器:https://www.ldmnq.com/安装 magisk:https://blog.csdn.net/Ruaki/article/details/135580772安装 Dia 插件 (作用:禁…

构建现代数据湖

现代数据湖是一半数据仓库和一半数据湖,对所有事情都使用对象存储。使用对象存储来构建数据仓库是通过 Open Table Formats OTF) 实现的,例如 Apache Iceberg、Apache Hudi 和 Delta Lake,这些规范一旦实现,就可以无缝…

【proteus经典项目实战】51单片机用计数器中断实现100以内的按键计数并播放音乐

一、简介 一个基于8051微控制器的计数器系统,该系统能够通过按键输入递增计数,并且能够在达到100时归零。该系统将使用计数器中断和外部中断来实现其功能。 51单片机因其简单易用和成本效益高,成为电子爱好者和学生的首选平台。通过编程单片…

大数据学习之Flink基础

Flink基础 1、系统时间与时间时间 系统时间(处理时间) 在Sparksreaming的任务计算时,使用的是系统时间。 假设所用窗口为滚动窗口,大小为5分钟。那么每五分钟,都会对接收的数据进行提交任务. 但是,这里有…

项目都做完了,领导要求国际化????--JAVA后端篇

springboot项目国际化相信各位小伙伴都会,很简单,但是怎么项目都做完了,领导却要求国际化文件就很头疼了 国际化的SpringBoot代码: 第一步:创建工具类 /*** 获取i18n资源文件** author bims*/ public class Message…

循环队列的实现【C语言】

用数组实现循环队列 题目:622. 设计循环队列 - 力扣(LeetCode) 分析 循环队列,队列满则不能再插入数据,队列为空则不能再出数据。 多开一个空间方便区分队列为空和队列为满的情况。 如果要存K个数据只开K个空间&a…

IEC104转BACnet网关:实现电力监控与楼宇自动化的无缝对接

在电力监控和楼宇自控领域,IEC104和BACnet作为两种重要的通信协议扮演着重要的角色。随着不同系统之间的数据交换与集成需求的不断增长,深圳市钡铼技术有限公司推出IEC104转BACnet网关来实现这两种协议之间的无缝转换,助力电力监控和楼宇自控…

leetcode日记(47)螺旋矩阵Ⅱ

这题思路不难,就是找规律太难了。 我首先的思路是一行一行来,根据规律填入下一行的数组,第i行是由前i个数字(n-2*i)个增序数列后i个数字组成,后来觉得太难找规律了就换了一种思路。 思路大致是先计算出需…

C#、Net6、WebApi报表方案

目录 1 Pdf表单方案 1.1出现如下错误提示: 1.2 字体路径使用 2 Docx报表模板方案 2.1 pdf方案缺陷 2.2 解决方案 3 Spire.Doc报表方案 3.1 Docx方案缺陷 3.2 解决方案 4 插入复选框 5 WebApi文件流下载接口 6 软件获取方式 1 Pdf表单方案 使用【Adobe Acrobat P…

MongoDB教程(二十三):关于MongoDB自增机制

💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 文章目录 引言一、MongoD…

raise JSONDecodeError(“Expecting value”, s, err.value) from None

raise JSONDecodeError(“Expecting value”, s, err.value) from None 目录 raise JSONDecodeError(“Expecting value”, s, err.value) from None 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页,我是…

ISO 50001能源管理体系:企业可持续发展的新引擎

在全球范围内,能源管理正成为企业战略规划的重要组成部分。ISO 50001能源管理体系(EnMS)作为一项国际标准,为企业提供了一套系统的方法,帮助其提高能源绩效,降低能源消耗,减少碳排放。通过有效实…

Feign-微服务通信(Feign远程调⽤ Feign简介 基本使⽤ ⾃定义配置 Feign使⽤优化)

目录 一、Feign远程调⽤ 二、Feign简介 三、基本使⽤ 1. 加⼊Fegin的依赖 2. 在主类上添加Fegin的注解 3. 创建⼀个service, 并使⽤Fegin实现微服务调⽤ 4. 修改controller代码,并启动验证 5. 重启order微服务,查看效果 四、⾃定义配置…

oncoPredict:根据细胞系筛选数据预测体内或癌症患者药物反应和生物标志物

在14年的时候,oncoPredict函数的开发团队在Genome Biology上发了一篇文章。 这篇文章的核心目的是阐释了使用治疗前基线肿瘤基因表达数据去预测患者化疗反应。开发团队发现使用细胞系去预测临床样本的药物反应是可行的。 鉴于之前的理论,该研究团队首先…

C语言开关迷宫

目录 注意事项开头程序程序的流程图程序的效果结尾 注意事项 程序里有关字符’\033’的输出都关于Sunshine-Linux的其中一篇博客——《printf函数高级用法设置打印字体颜色和背景色等》 开头 大家好,我叫这是我58。今天,我们来看一下我用C语言编译的开…

昇思25天学习打卡营第23天|munger85

FCN图像语义分割 FCN-8s表示从8倍下采样的特征图通过上采样恢复到原图尺寸的网络结构。现在下载数据集,这种8倍的比较好。 from download import download url “https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/dataset_fcn8s.t…

2024年用户口碑超佳的硬U盘数据恢复工具榜

U盘一个体积小小却能携带大量文件、视频的日常存储好物,你肯定也有吧。不知道你有没有遇到过U盘里数据丢失的情况,这时候U盘数据恢复工具就能帮我们走出这种困境。 1.U盘F0XIT数据恢复大师 一键直达>>https://www.pdf365.cn/foxit-restore/ 这…

自动驾驶-机器人-slam-定位面经和面试知识系列05之常考公式推导(02)

这个博客系列会分为C STL-面经、常考公式推导和SLAM面经面试题等三个系列进行更新,基本涵盖了自己秋招历程被问过的面试内容(除了实习和学校项目相关的具体细节)。在知乎和牛客(牛客上某些文章上会附上内推码)也会同步…