redis watchdog_Redis源码分析09——定时事件

在前文说道,服务端编程中主要涉及到三类事件:IO事件、定时(一次或多次)事件以及信号事件。在这里将介绍常用的三类处理方式,并分析Redis选择哪种。

常见定时方法

SIGALRM信号

原理

调用alarm或者settimer函数,他在指定超时期满时会产生SIGALRM信号。我们为其建立一个信号处理函数,执行相应的处理逻辑。

代码

  1. 使用alarm实现带超时的connect
static 
  • alarm(0)表示取消alarm信号
  1. Redis使用settimer实现WatchDog
  • 函数原型
#include 
  • 特点

setitimer 能够在 Timer 到期之后,自动再次启动自己,因此,用它来解决 Single-Shot Timer 和 Repeating Timer 的问题显得很简单。

  • 具体代码
void 

分析

  1. 优点

可以看出使用SIGALRM实现简单。

  1. 缺点

信号属于异步事件,在多线程条件下,还需考虑信号重入问题。因此我们并不推荐使用。

使用socket选项

原理

借助socket的SO_RCVTIMEO和SO_SNDTIMEO选项,分别设置socket接受超时时间和发送数据超时时间。其中常见API支持该选项的情况如下:

v2-e30ff3181d6d92b3174b1efe63c85eb3_b.jpg

代码

void

分析

  1. 优点

使用简单。只需一次设置。

  1. 缺点
  • 使用场景有限,仅支持socket超时
  • 并非所有实现都支持

使用IO复用

原理

Linux下的3组IO复用函数都带有超时参数,因此他不仅能统一处理信号和IO事件,也可以处理定时事件。但是因为IO复用事件触发可能在超时时间到期之前就返回(如有可读可写事件),因此我们需要不断更新定时参数以反映剩余时间。

/*select系列*/
  • select中timeval数据结构如下
struct 
  • 对于select而言
1
  • 对于poll和epoll而言
如果

代码

int

分析

  1. 优点

将定时事件统一由IO复用管理,实用而方便。实际中也是使用这个方法。

  1. 缺点

暂无

常见定时任务管理方法

在服务端的网络模型中,定时任务主要包括时间(相对或者绝对均可——redis采用的是绝对时间)以及回调函数,尽管不同放大,但是思想基本相同。

无序链表-Redis为例

数据结构

typedef 

操作

  1. 创建

redis中最重要的定时函数且是周期执行的函数,就是大名鼎鼎的serverCron函数。在redis中由于定时任务比较少,因此并没有严格的按照过期时间来排序的,而是按照id自增+头插法来保证基本有序。

if 

v2-c9fd574a18fead1624f22ac4c6b9c244_b.jpg
  1. 触发

redis中是采用IO复用来进行定时任务的。

  • 查找距离现在最近的定时事件,见aeSearchNearestTimer
static 

这里时间复杂度可能比较高,实际中需要结合具体场景使用。

  • 更新剩余过期时间,想想为啥呢?因为我们前面提到过,io复用有可能因为IO时间返回,所以需要更新。
if 
  1. 执行定时事件

一次性的执行完直接删除,周期性的执行完在重新添加到链表。

/* Process time events */

优缺点

  1. 优点

实现简单

  1. 缺点

如果定时任务很多,效率比较低。

升序链表

参照上述无序列表

时间轮

时间轮使用了哈希表的思想,将定时器散列在不同的链表上,这样可以保证每条链表上的定时器数少于排序链表上的定时器数量,其基本思想如下:

v2-5debcf3a2ec077e3f8f94273fa2f330c_b.jpg
  • si:表示一个时间周期,即心博时间
  • N:表示槽总数。转动一周时间为N*si
  • cs:表示当前指向的槽。则添加一个定时器ts=(cs+(ti%si))%N

数据结构

struct 

操作

  1. 添加
tw_timer
  1. 触发
void 

优缺点

  1. 优点
  • 时间轮使用哈希表的思想,优化了纯链表带来的插入性能较低问题。
  • 容易扩展,当需要精细精度时,我们可以采用多层时间轮,参照kafka的三层时间轮。
  1. 缺点
  • 只能以固定频率转动,若要支持不同精度的定时器,单个时间轮可能会造成溢出或者耗费大量内存。因此需要引入多层,但却加大了实现的难度。

时间堆

根据前面描述,我们很容易联想到时间堆,而且还是小根堆

数据结构

typedef 

操作

  1. 添加
int 
  1. 触发
if

pop_timeout函数

int 

分析

  1. 优点

支持各种精度的定时器。

  1. 缺点
  • 定时任务过多,插入和删除时间复杂度可能比较高。此时可以使用多叉树进行优化。

扩展

除了上述几种方法,还有Nginx支持的红黑树,Redis的zset等等均可。

总结

  1. 在常见的定时方法中,推荐使用IO复用,因为可以进行统一处理。
  2. Redis中由于定时任务比较少,而且是单线程,所以直接就采用链表,且基本有序。
  3. 项目中推荐使用IO复用+时间堆的方式。
  4. 如果定时任务很多,可以参考kafka的多层时间轮。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值