定时器设计

news2024/11/17 13:39:22

定时器设计

定时器应用:

  • 游戏的Buff实现,Redis中的过期任务,Linux中的定时任务等等
  • 心跳检测,如服务器接收队列满了,tcp客户端会定时探测是否能够发送数据

定时器数据结构选取要求:

  • 需要快速找到到期任务,因此,应该具有时间有序性
  • 其过期执行、插入(添加定时任务)和删除(取消定时任务)的频率比较高,三种操作效率必须保证

各种数据结构的时间复杂度:

  • 最小堆:插入O(logn),删除O(logn),过期expire执行O(1)

  • 红黑树:插入O(logn),删除O(logn),过期expire执行O(logn)

  • 哈希表+链表(时间轮):插入O(1),删除O(1),过期expire平均执行O(1)(最坏为O(n))

不同开源框架定时器实现方式不一,如,libuv采用最小堆来实现,nginx采用红黑树实现,linux内核和skynet采用时间轮算法实现等等。

其中执行到期任务有两种工作方式:

  • 轮询: 每隔一个时间片去查找哪些任务到期
  • 睡眠/唤醒:不停查找deadline最近任务,到期执行,否则sleep;sleep期间,任务有改变,线程会被唤醒

定时器和的使用:

  • 第一种,网络事件和时间事件在一个线程当中配合使用;例如nginx、redis
while (!quit) {
    int now = get_now_time();// 单位:ms
    int timeout = get_nearest_timer() - now;
    if (timeout < 0) timeout = 0;
    int nevent = epoll_wait(epfd, ev, nev, timeout);   // 时延
    for (int i=0; i<nevent; i++) {   // 时延
        //... 网络事件处理
    }
    update_timer(); // 时间事件处理,  到这里时延很大,怎么办, nginx到时间了会去打断epoll_wait(),红黑树+定时信号
}
  • 第二种 在其他线程添加定时任务
void* thread_timer(void * thread_param) {
    init_timer();
    while (!quit) {
        update_timer(); // 更新检测定时器,并把定时事件发送到消息队列中
        sleep(t); // 这里的 t 要小于 时间精度
    }
    clear_timer();
    return NULL;
}
pthread_create(&pid, NULL, thread_timer, &thread_param);

红黑树(nginx)

以时间作为key,时间是一样的话,红色树本身就支持key相等,就看你key相等节点放左还放右,最好放右,因为先插入的先执行,放左边


int find_nearest_expire_timer() {
    ngx_rbtree_node_t  *node;
    // 哨兵节点,红黑树是空的(红黑树的叶子节点都指向这个哨兵节点,哨兵节点是黑色的,红黑树的所有叶子节点都是黑色的)
    if (timer.root == &sentinel) {
        return -1;
    }
    node = ngx_rbtree_min(timer.root, timer.sentinel);
    int diff = (int)node->key - (int)current_time();  
    return diff > 0 ? diff : 0;
}

最小堆(boost.asio、go、libuv)

只关心父子节点的大小关系,不关系兄弟之间的大小关系

20230215204258

最小堆利用数组存储(因为是完全树):

20230215204410

索引方式:

20230215204658

效率比红黑高,增删简单,找最小节点快,就是第一个O(1),而红黑树的速度是O(h),最差情况要找h次

增删操作:二者都是log(n),但是最小堆更稳定,因为是完全树,左右子树高度差最大为1,红黑树是相差2倍

时间轮 (kafaka、netty)

kafaka时间轮

单层时间轮:可用来做时间窗口(限流、熔断),以轮的形式进行时间复用

限流和熔断的区别:

比如:5s内只能做100次操作

限流:如tcp滑动窗口

20230215210925 20230215211047

每秒移动一下,反正这个窗口内只能做500次操作

熔断:

20230215211157

先算0-5s内的,再算5-9s内的,这些个时间区间内只能做100次操作

如:nigix可以配置1秒内只接收10个包,否则认为对方在对我进行DDOS攻击

单层时间轮

应用:

  • kv数据库热key检测;
  • 心跳检测:客户端每 5 秒钟发送心跳包;服务端若 10 秒内没收到心跳数据或其他请求,则清除连接

确定时间轮的大小:

比如我的时间轮大小是8,我在5s的时候检测了,那我下次检测时间因该是(5+10)%8 =7 ,那就不对了,本来是隔10s检测的,现在变成隔2s检测了,没有检测到超时事件

20230215213455

但其实,索引为0的位置已经有2个超时事件了

时间轮大小确定方式:2^n>10 ,这里就应该设置为16

时间轮大小不能设置太大(时间精确设置太小也会导致时间轮太大),不然会出现空推进问题,也就是在事件数量较少时,走时间轮的时候,能多地方都是空的。在分布式定时器中需要解决这个问题。 =》 最小堆+单层级时间轮 ,最小堆告诉时间轮下一次要检测的时间,不要一格一格去找了

多层时间轮

当时间跨度很大,精度不能固定时,用多层时间轮,将精度小(最近触发)的放内层,如秒,而分、时 放外层

20230215214845

任务添加方法:

60s内触发的任务放第一层,60s后触发的放第二层,3600后的放第三层,如61s触发的事件就放第二层第一个位置,这里记为A

20230215215259

当时间走啊走,走了50s,也就是11s后A位置的事件要触发了,这时候就将这个事件移动到第一层的第11个位置。

同理,分针层一分钟移动一次,后面的也往前移动,如原本2分后触发的事件现在要往前移动一个位置;时针层一小时移动一次(原本都是一秒移动一次)。这样就解决了空推进的问题,只关注最近发生的事件

第一层0号元素有数据,第二第三都是没数据的,但有的开源框架也有,因为有最大值的限制,比如unint32最大值只能为2^32-1,超过这个数的事件,都放在第0个节点:

20230215220942

多线程使用时间轮的优势

加锁时,锁的粒度小 (就是线程占用锁的时间,时间越小,粒度越小,并发量越高) =》 所以大量数据需要采用时间轮

因为对定时器加锁时,需要锁整个结构,如果采用红黑树和最小堆,时间复杂度时O(log),加锁复杂,但是时间轮的增查都是O(1)操作,取余就行。不能删除,但删除的问题可以在一个事件里添加是否执行的标志,如果被其他线程执行了,就不执行了,直接return

lock(&mtx);
操作数据结构
unlock(&mtx);

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

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

相关文章

防火墙设置实验(12)

预备知识防火墙基本原理 防火墙是一种访问控制技术&#xff0c;位于可信与不可信网络之间&#xff0c;通过设置一系列安全规则对两个网络之间的通信进行控制&#xff0c;检测交换的信息&#xff0c;防止对信息资源的非法存取和访问。防火墙分为软防火墙和硬防火墙&#xff0c;一…

关于分布式的一些基础知识

1、分布式锁 (ngix,zoomkeeper,raft,kafka) 在单机场景下&#xff0c;可以使用语言的内置锁来实现进程或者线程同步。但是在分布式场景下&#xff0c;需要同步的进程可能位于不同的节点上&#xff0c;那么就需要使用分布式锁。 为了保证一个方法或属性在高并发情况下的同一时间…

【面试题】前端春招第二面

不容错过的一些面试题小细节&#xff0c;话不多说&#xff0c;直接看题~大厂面试题分享 面试题库后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★地址&#xff1a;前端面试题库HTML/CSS/Javascript/ES篇&#xff08;1&#xff09;标准盒模型和怪异盒…

k8s集群概念组件简介

一、Kubernetes 集群概述1.2 Kubernetes 资源资源用途服务&#xff08;Service&#xff09;Kubernetes 使用服务在一组 pod 上公开正在运行的应用程序。ReplicaSetKubernetes 使用 ReplicaSet 来维护恒定的 pod 号。Deployment维护应用程序生命周期的资源对象。1.3 Kubernetes …

重生之我是赏金猎人-SRC漏洞挖掘(七)-看我如何从FUZZ到SRC官网XSS

0x01 前奏 本文来自&#xff1a;RGM78sec 截至发稿&#xff0c;vuln早已修复 前不久在挖掘某SRC时&#xff0c;偶然在该SRC官网的编辑器发现了一个接口。 起初以为是任意文件包含能RCE了&#xff0c;后来测试发现只是拼接读取了远程资源站的图片&#xff0c;原本都想着放弃…

TypeScript(二)基本数据类型

前言 距离上篇博客已有两年了&#xff0c;对TS也有了一定的认识&#xff0c;于是计划继续更新系列文章 基本数据类型 在JS中&#xff0c;基本数据类型有&#xff1a;Boolan&#xff08;布尔&#xff09;&#xff0c;Number&#xff08;数字&#xff09;&#xff0c;String&a…

什么是事务?Mysql事务怎么用?Mybatis怎么操作事务的?Spring呢?快进来看看

目录 一、计算机中的事务 1. 概念 2.事务的ACID特性 3.事务类型 1)手动事务 2)自动事务 4.为什么需要事务? 5.优点 二、MySQL事务 1.语法格式: 2.关闭自动提交&#xff0c;开启新事务 3.开启自动提交&#xff0c;关闭事务控制 三、Mybatis事务 Mybatis开关事务控…

Meta AR眼镜主管:正开发史无前例的AR,但要解决很多困难

前不久&#xff0c;Meta CTO Andrew Bosworth在个人博客上“怒斥”公司内部不够专注&#xff0c;应该将资源投入在有核心竞争力、高投资回报率的业务上&#xff0c;而不是开发取悦用户却不赚钱的产品。尽管删除一些小众功能后&#xff0c;用户可能会不满&#xff0c;但为了让Me…

Bi系统跟数据中台的区别是什么?

随着数据时代的发展&#xff0c;BI分析是当今数据时代必不可少的能力之一。BI系统通过系统化产品化的方法&#xff0c;能够大幅降低数据的获取成本、提升数据使用效率。同时借助可视化、交互式的操作&#xff0c;可以高效支持业务的分析及发展。 BI如此火热&#xff0c;随之而…

openpnp - 增加散料飞达

文章目录openpnp - 增加散料飞达概述笔记飞达在设备上的放置方向编带料在飞达中的方向将相机移动到飞达料的第一孔的中心捕获保存第1孔中心编带设置测试相机位置是否正确测试吸嘴的位置是否正确将吸嘴回到安全位置测试是否能正确抓料ENDopenpnp - 增加散料飞达 概述 使用open…

MySQL性能调优与设计——MySQL调优——慢查询

MySQL调优–慢查询 MySQL调优金字塔 很明显从图上可以看出&#xff0c;越往上走&#xff0c;难度越来越高&#xff0c;收益却越来越小。 对于架构调优&#xff0c;在系统设计时首先需要充分考虑业务的实际情况&#xff0c;是否可以把不适合数据库做的事情放到数据仓库、搜索引…

分享111个HTML旅游交通模板,总有一款适合您

分享111个HTML旅游交通模板&#xff0c;总有一款适合您 111个HTML旅游交通模板下载链接&#xff1a;https://pan.baidu.com/s/1VHJSBVJbj4PQpPAwxysJBg?pwd8b17 提取码&#xff1a;8b17 Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 汽车租赁平台网页模板 汽…

时的科技迎1亿融资,这辆“空中的士”能否实现真正飞行?

近期&#xff0c;进行载人eVTOL的研发、生产和销售的时的科技宣布完成1亿元Pre-A轮融资&#xff0c;成立不到两年&#xff0c;这已是时的科技的第三轮融资&#xff0c;此前&#xff0c;时的科技已获得蓝驰创投和德迅投资千万美元种子轮投资。在不少人看来&#xff0c;时的科技所…

恢复出厂设置在哪里?抓住这份官方指南(Win10电脑)

当电脑经常报告错误&#xff0c;并伴有运行缓慢和停滞等问题时&#xff0c;很可能是计算机老化造成的。想要解决这个问题&#xff0c;可以通过恢复出厂设置使得电脑恢复成初始状态&#xff0c;自然而然问题就解决了。 但是很多人不知道&#xff0c;恢复出厂设置在哪里&#xf…

【自学Linux】Linux运行级别

Linux运行级别 Linux运行级别教程 Linux 可以支持运行级别的设置&#xff0c;运行级别就是操作系统当前正在运行的功能级别&#xff0c;级别是从 0 到 6。Centos7 系统之前的版本是通过 /etc/inittab 文件来定义系统&#xff0c;而 CentOS7 用的是 /lib/systemd/system/runle…

MySQL数据库调优————JOIN优化

JOIN的种类 LEFT JOIN SELECT <select_list> FROM Table_A A LEFT JOIN Table_B B ON A.Key B.Key求的是A所有的数据以及A与B的交集 RIGHT JOIN SELECT <select_list> FROM Table_A A RIGHT JOIN Table_B B ON A.Key B.Key求的是B所有的数据以及A和B的交集 IN…

Nginx 新增模块 http_image_filter_module 来实现动态生成缩略图

前言 通过 nginx 的 HttpImageFilterModule 模块裁剪过大的图片到指定大小&#xff0c;这个nginx自带的模块是默认关闭的&#xff0c;所以需要重新编译nginx加上此模块。 一、编译 nginx 1.查看 nginx 模块 由于nginx 是之前装好的&#xff0c;这里需要先看一下是否安装了H…

Sentinel简单使用

目录 1.官网 2.主要作用 3.安装启动 3.功能详细 3.1实时监控 3.2 簇点链路 3.3流控规则 3.4熔断规则 3.4热点key限流 3.5系统规则 4.SentinelResource注解解释 1.官网 gitHub GitHub - alibaba/Sentinel: A powerful flow control component enabling reliability…

尚医通 (十六)搭建平台用户系统前端环境 | 用户平台首页数据

目录一、服务端渲染技术NUXT1、什么是服务端渲染2、什么是NUXT二、NUXT环境初始化1、下载压缩包2、解压3、修改package.json4、修改nuxt.config.js5、在命令提示终端中进入项目目录6、安装依赖7、测试运行8、NUXT目录结构9、封装axios10、引入element-ui三、首页静态数据整合1、…

Docker镜像发布到阿里云和私有库

目录 一、Docker镜像 &#xff08;一&#xff09;概述 &#xff08;二&#xff09;Docker镜像加载原理 &#xff08;三&#xff09;镜像分层结构优势 &#xff08;四&#xff09;重点理解 &#xff08;五&#xff09;docker commit操作实例 &#xff08;六&#xff09;总…