Beanstalkd源码分析—事件超时的处理

版权声明:本文为博主原创文章,未经博主允许不得转载。 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函数

超时完成的工作原理如下

  1. 遍历所有的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步。

  2. 遍历所有的tube,然后执行以下操作:

    取一个tube,用tube的deadline_at减去(ttr+now())和现在的时间,得到差值,然后执行以下流程:

    • 若差值小于0(说明超时)且pause的值大于0,则执行process_queue()。
    • 若差值大于0,则重置事件的超时时间。
  3. 遍历server结构的连接,执行以下操作:

    从连接堆中取一个连接,查看该连接是否超时(阻塞超时),然后按以下流程执行:

    • 若没有超时((d=c->tickat-now) > 0),重设事件驱动的超时时间为d和当前值的最小者,并跳到第4步
    • 若超时,把该连接从server的连接堆中删除,并执行函数:conn_timeout。
  4. 执行update_conns函数:

  5. 返回的是最小的超时时间。

公共函数的执行原理

  • process_queue()函数
    该函数执行流程如下:

    1. 遍历所有tube,若有连接在等待该tube,就从该tube中选出一个优先级(pri)最小的job
    2. 把该job从对应tube的ready堆中删除
    3. 把该job添加到对应conn结构的reserved_jobs链表中。
  • conn_timeout()函数

    1. 客户端是否处于等待状态(当执行reserve命令时,客户端可能进入等待状态)
    2. 从conn的reserved_jobs链表中选择一个过期时间最小的job,把should_timeout设置为1
    3. 从reserved_jobs链表中找到一个ttr最小的job
    4. 若job还没有过期,直接跳到后面的步骤
    5. 若job正在被写出,需要复制一份job,并保留新的地址
    6. 把job放入对应队列中
    7. 重置连接的过期时间
  • update_conns()函数

    1. 遍历dirty连接队列
    2. 重置该队列连接的期望状态

总结

本文讲述了beanstalkd事件驱动的超时时,完成的工作。

猜你喜欢

转载自blog.csdn.net/zg_hover/article/details/82315164