版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zg_hover/article/details/82315164
概述
在多路复用的网络编程中,比如select,epoll,poll等,往往会等待一定的时间,来允许读,写,异常的事件发生。当超时还没有事件发生时,会完成一定的处理任务。而超时处理的任务也非常重要。
本文介绍beanstalkd的超时处理部分的实现原理。
进入超时处理
在beanstalkd中,事件超时的处理在prottick函数中完成。前面的文章分析过命令处理流程,我们再来看一下事件处理的代码:
void srvserve(Server *s) {
...
for (;;) {
// 若没有读,写,异常事件发生会进入该函数
period = prottick(s);
// 等待事件处理
int rw = socknext(&sock, period);
if (rw == -1) {
twarnx("socknext");
exit(1);
}
// 处理事件
if (rw) {
sock->f(sock->x, rw);
}
}
...
}
超时完成的工作:prottick函数
超时完成的工作原理如下
遍历所有的tube,从每个tube的delay堆中选出一个超时时间(deadline_at=ttr+now())最小的job,然后执行以下步骤:
1.1. 和现在的时间比较,若该job没有过期,重置epoll的事件超时时间为:该job的超时时间和一个固定值(这里是一小时)的最小值,然后跳到第2步。
1.2. 若job已经过期,把该job从所属tube的delay堆中删除,并把job插入到该job所属tube的ready堆中,把job的状态改成Ready,然后执行process_queue函数(该函数的功能后面会有说明),接下来执行第2步。
遍历所有的tube,然后执行以下操作:
取一个tube,用tube的deadline_at减去(ttr+now())和现在的时间,得到差值,然后执行以下流程:
- 若差值小于0(说明超时)且pause的值大于0,则执行process_queue()。
- 若差值大于0,则重置事件的超时时间。
遍历server结构的连接,执行以下操作:
从连接堆中取一个连接,查看该连接是否超时(阻塞超时),然后按以下流程执行:
- 若没有超时((d=c->tickat-now) > 0),重设事件驱动的超时时间为d和当前值的最小者,并跳到第4步
- 若超时,把该连接从server的连接堆中删除,并执行函数:conn_timeout。
执行update_conns函数:
- 返回的是最小的超时时间。
公共函数的执行原理
process_queue()函数
该函数执行流程如下:- 遍历所有tube,若有连接在等待该tube,就从该tube中选出一个优先级(pri)最小的job
- 把该job从对应tube的ready堆中删除
- 把该job添加到对应conn结构的reserved_jobs链表中。
conn_timeout()函数
- 客户端是否处于等待状态(当执行reserve命令时,客户端可能进入等待状态)
- 从conn的reserved_jobs链表中选择一个过期时间最小的job,把should_timeout设置为1
- 从reserved_jobs链表中找到一个ttr最小的job
- 若job还没有过期,直接跳到后面的步骤
- 若job正在被写出,需要复制一份job,并保留新的地址
- 把job放入对应队列中
- 重置连接的过期时间
update_conns()函数
- 遍历dirty连接队列
- 重置该队列连接的期望状态
总结
本文讲述了beanstalkd事件驱动的超时时,完成的工作。