首页 > 编程语言 >Swoole 源码分析之 Timer 定时器模块

Swoole 源码分析之 Timer 定时器模块

时间:2024-04-07 23:00:29浏览次数:25  
标签:定时器 Swoole swoole msec tnode timer 源码 Timer fci

原文首发链接:Swoole 源码分析之 Timer 定时器模块
大家好,我是码农先森。

引言

Swoole 中的毫秒精度的定时器。底层基于 epoll_waitsetitimer 实现,数据结构使用最小堆,可支持添加大量定时器。

在同步 IO 进程中使用 setitimer 和信号实现,如 ManagerTaskWorker 进程,在异步 IO 进程中使用 epoll_wait/kevent/poll/select 超时时间实现。

定时器的添加和删除,全部为内存操作。在官方的基准测试脚本中,添加或删除 10 万个随机时间的定时器耗时为 0.08s 左右,因此性能是非常高效的。

源码拆解

我们在分析源代码之前,先看这段使用定时器的代码。Timer::after 函数是设置一个一次性的定时器,也就是执行一次就结束了,常用于执行一次性任务的场景。Timer::tick 函数会每间隔一段时间执行一次,类似一个闹钟的机制,常用于需要定时执行任务的场景。

<?php
// 设置一个一次性定时器
Swoole\Timer::after(1000, function(){
    echo " timer after timeout\n";
});

// 设置一个间隔时钟定时器
Swoole\Timer::tick(1000, function(){
    echo "timer tick timeout\n";
});

按照之前分析源代码的策略,先对整个源码的调用流程进行梳理,以便于让我们有个整体的印象,调用流程如下图所示。

swoole_timer.cc 这个源码文件中定义了两个函数 swoole_timer_afterswoole_timer_tick。从这段代码中可以看出唯一的区别是,在调用 timer_add 函数时的传参有所不同,一个是 false,一个是 true,表示的是是否需要持久化的执行任务。另外 timer_add 函数实现了一些根据细化的逻辑,例如:参数的解析、一些检查判断的工作。最后,根据 persistent 参数判断是否执行持久化的操作。

// 定义 PHP 函数 swoole_timer_after
// swoole-src/ext-src/swoole_timer.cc:221
static PHP_FUNCTION(swoole_timer_after) {
    timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
}

// 定义 PHP 函数 swoole_timer_tick
// swoole-src/ext-src/swoole_timer.cc:225
static PHP_FUNCTION(swoole_timer_tick) {
    timer_add(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
}

// 添加定时任务到定时器中, 并根据持久性标志判断是否需要一直执行
// swoole-src/ext-src/swoole_timer.cc:155
static void timer_add(INTERNAL_FUNCTION_PARAMETERS, bool persistent) {
    zend_long ms;
    Function *fci = (Function *) ecalloc(1, sizeof(Function));
    TimerNode *tnode;
    
    // 解析参数
    ZEND_PARSE_PARAMETERS_START(2, -1)
    Z_PARAM_LONG(ms)
    Z_PARAM_FUNC(fci->fci, fci->fci_cache)
    Z_PARAM_VARIADIC('*', fci->fci.params, fci->fci.param_count)
    ZEND_PARSE_PARAMETERS_END_EX(goto _failed);

    // 检查定时器值 ms 是否小于预定义的最小值 SW_TIMER_MIN_MS
    if (UNEXPECTED(ms < SW_TIMER_MIN_MS)) {
        php_swoole_fatal_error(E_WARNING, "Timer must be greater than or equal to " ZEND_TOSTR(SW_TIMER_MIN_MS));
    _failed:
        efree(fci);
        RETURN_FALSE;
    }

	// 进行额外的检查
    // no server || user worker || task process with async mode
    if (!sw_server() || sw_server()->is_user_worker() ||
        (sw_server()->is_task_worker() && sw_server()->task_enable_coroutine)) {
        php_swoole_check_reactor();
    }

    // 使用指定的毫秒数、持久性标志、回调函数 timer_callback 和函数指针 fci 添加一个定时器
    tnode = swoole_timer_add((long) ms, persistent, timer_callback, fci);
    if (UNEXPECTED(!tnode)) {
        php_swoole_fatal_error(E_WARNING, "add timer failed");
        goto _failed;
    }
	
	// 为定时器节点 tnode 设置类型和析构函数
    tnode->type = TimerNode::TYPE_PHP;
    tnode->destructor = timer_dtor;

    // 根据持久性标志,会一直执行定时的任务
    if (persistent) {
        if (fci->fci.param_count > 0) {
            uint32_t i;
            zval *params = (zval *) ecalloc(fci->fci.param_count + 1, sizeof(zval));
            for (i = 0; i < fci->fci.param_count; i++) {
                ZVAL_COPY(&params[i + 1], &fci->fci.params[i]);
            }
            fci->fci.params = params;
        } else {
            fci->fci.params = (zval *) emalloc(sizeof(zval));
        }
        fci->fci.param_count += 1;
        ZVAL_LONG(fci->fci.params, tnode->id);
    } else {
    	// 只会执行一次
        sw_zend_fci_params_persist(&fci->fci);
    }
    sw_zend_fci_cache_persist(&fci->fci_cache);
    RETURN_LONG(tnode->id);
}

timer.cc 源码文件中 swoole_timer_add 这个函数会检查是否已经有可用的定时器管理对象,如果没有的话会进行实例化创建一个,然后通过 SwooleTG.timer->add() 方法添加一个定时器任务。

// 这段代码用于添加一个定时器到 Swoole 框架中的定时器管理器中
// swoole-src/src/wrapper/timer.cc:40
TimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callback, void *private_data) {
    // 这里检查定时器是否可用
    if (sw_unlikely(!swoole_timer_is_available())) {
    	// 如果定时器不可用,则会创建一个新的对象
        SwooleTG.timer = new Timer();
        // 并对其进行初始化
        if (sw_unlikely(!SwooleTG.timer->init())) {
            // 若初始化失败,就会释放内存
            delete SwooleTG.timer;
            SwooleTG.timer = nullptr;
            return nullptr;
        }
    }
    // 调用定时器对象的 add 方法,向定时器中添加一个定时器
    return SwooleTG.timer->add(ms, persistent, private_data, callback);
}

这个函数 *Timer::add 会构建一个新的定时器节点,并且设置一些属性值,例如:类型、执行时间、回调函数等。最后,会将定时器节点加入到最小堆的数据结构中。

// 用于向定时器管理器中添加一个新的定时器节点
// swoole-src/src/core/timer.cc:106
TimerNode *Timer::add(long _msec, bool persistent, void *data, const TimerCallback &callback) {
    // 检查传入的毫秒数 _msec 是否小于等于 0
    if (sw_unlikely(_msec <= 0)) {
        swoole_error_log(SW_LOG_WARNING, SW_ERROR_INVALID_PARAMS, "msec value[%ld] is invalid", _msec);
        return nullptr;
    }

	// 获取当前相对毫秒数,并检查其是否小于 0
    int64_t now_msec = get_relative_msec();
    if (sw_unlikely(now_msec < 0)) {
        return nullptr;
    }
	
	// 创建一个新的定时器节点 tnode
	// 并设置节点的数据、类型、执行时间、间隔、状态、回调函数、轮数以及析构函数
    TimerNode *tnode = new TimerNode();
    tnode->data = data;
    tnode->type = TimerNode::TYPE_KERNEL;
    tnode->exec_msec = now_msec + _msec;
    tnode->interval = persistent ? _msec : 0;
    tnode->removed = false;
    tnode->callback = callback;
    tnode->round = round;
    tnode->destructor = nullptr;

	// 更新下一个计划触发时间
	// 如果当前没有下一个计划或者新的时间比当前下一个计划更早
	// 则更新为新的时间。
    if (next_msec_ < 0 || next_msec_ > _msec) {
        set(this, _msec);
        next_msec_ = _msec;
    }

	// 给定时器节点分配一个唯一的ID
    tnode->id = _next_id++;
    if (sw_unlikely(tnode->id < 0)) {
        tnode->id = 1;
        _next_id = 2;
    }
	
	// 将节点加入堆中,同时更新堆的索引
    tnode->heap_node = heap.push(tnode->exec_msec, tnode);
    if (sw_unlikely(tnode->heap_node == nullptr)) {
        delete tnode;
        return nullptr;
    }

    // 记录节点信息
    map.emplace(std::make_pair(tnode->id, tnode));
    swoole_trace_log(SW_TRACE_TIMER,
                     "id=%ld, exec_msec=%" PRId64 ", msec=%ld, round=%" PRIu64 ", exist=%lu",
                     tnode->id,
                     tnode->exec_msec,
                     _msec,
                     tnode->round,
                     count());
                     
    // 返回新添加的定时器节点
    return tnode;
}

总结

  • Swoole 中实现了毫秒精度的定时器,而原生的 PHP 中只支持到秒级别。
  • 数据结构使用最小堆支持添加大量定时器,全部为内存操作且十分高效。
  • 定时器在实际的业务场景中应用也是非常广泛,常用于延时或定时执行的任务中,例如:订单超时未付款自动取消等场景。

标签:定时器,Swoole,swoole,msec,tnode,timer,源码,Timer,fci
From: https://www.cnblogs.com/yxhblogs/p/18090625

相关文章

  • Springboot计算机毕业设计财务报销微信小程序【附源码】开题+论文+mysql+程序+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着移动互联网技术的飞速发展,微信小程序作为一种新型的应用形态,以其便捷、高效的特点受到了广大用户的青睐。在高等教育领域,财务管理是学校运营中不......
  • Springboot计算机毕业设计博物馆预约小程序【附源码】开题+论文+mysql+程序+部署
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景在信息化、数字化日益发展的今天,博物馆作为传承历史文化的重要场所,其管理和服务方式也在不断革新。传统的博物馆参观方式往往受限于开放时间、参观人......
  • 【MATLAB源码-第172期】基于matlab的小波变换能量率BP神经网络的机械轴承故障分析以及
    操作环境:MATLAB2022a1、算法描述在现代工业生产中,轴承是最为常见和关键的机械基础部件之一,其性能状态直接影响着整个机械系统的稳定性和可靠性。由于轴承在运行过程中不断承受高负荷和摩擦,故障发生的概率相对较高。轴承故障的早期诊断对于预防严重机械事故、提高生产效率、......
  • 【MATLAB源码-第173期】基于matlab的RS编码的2FSK通信系统误码率仿真,通过AWGN信道输出
    操作环境:MATLAB2022a1、算法描述通信系统的基本框架在现代通信系统中,数据的传输通常涉及四个基本步骤:源编码、信道编码、调制和传输。源编码主要负责压缩数据,减少传输的数据量。信道编码则通过添加冗余信息来提高传输数据的可靠性。调制是将数字信号转换为适合在物理信道......
  • Timer定时器———创建定时器实例
    1.编写时间写入日志文件的脚本vimsystem.sh#!/bin/bashecho`date`>>/root/system.txtcatsystem.sh2.给脚本增加可执行权限chmod+xsystem.sh3.执行刚刚编写的脚本.system.sh4.查看日志文件,脚本执行成功catsystem.txt5.创建一个boot_backup.service文件,......
  • 【26.0】Django框架之settings源码
    【一】Django配置文件介绍Django框架默认提供给我们一个配置文件在我们项目根目录下的setting.py文件中,在里面我们可以看到很多的配置项并且我们能够自主的添加相应的配置但是其实这个文件只是Django暴露出来给我们的一个接口文件,在Django内部还存在一个更加强大的配置文件......
  • python计算机毕设【附源码】汉服文化管理系统(django+mysql+论文)
    本系统(程序+源码)带文档lw万字以上  文末可获取本课题的源码和程序系统程序文件列表系统的选题背景和意义选题背景:汉服,作为中国古代汉族传统服饰的总称,承载了丰富的历史文化遗产和审美价值。近年来,随着国民文化自信心的提升和传统文化复兴的浪潮,汉服文化逐渐走进了公众的......
  • 分享一个Python爬虫入门实例(有源码,学习使用)
    一、爬虫基础知识Python爬虫是一种使用Python编程语言实现的自动化获取网页数据的技术。它广泛应用于数据采集、数据分析、网络监测等领域。以下是对Python爬虫的详细介绍:架构和组成:下载器:负责根据指定的URL下载网页内容,常用的库有Requests和urllib。解析器:用于解析......
  • HIS系统是什么?一套前后端分离云HIS系统源码 接口技术RESTful API + WebSocket + WebSe
    HIS系统是什么?一套前后端分离云HIS系统源码接口技术RESTfulAPI+WebSocket+WebService医院管理信息系统(全称为HospitalInformationSystem)即HIS系统。常规模版包括门诊管理、住院管理、药房管理、药库管理、院长查询、电子处方、物资管理、媒体管理等,为医院管理提......
  • 企业工程项目管理系统源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管
     工程项目管理软件(工程项目管理系统)对建设工程项目管理组织建设、项目策划决策、规划设计、施工建设到竣工交付、总结评估、运维运营,全过程、全方位的对项目进行综合管理   工程项目各模块及其功能点清单一、系统管理    1、数据字典:实现对数据字典标签的增删改查操......