有大佬实现过不阻塞的定时器服务吗

lcmg

问题描述

比如我有很多定时器,有指定时间执行的比如每天0点28分执行【28 0 】,有固定间隔时间段执行的比如每五分钟执行异常【0 /1 】,还可以动态的添加管理这些定时器

为此你搜索到了哪些方案及不适用的原因

有参考过插件市场的yzh52521/webman-task,启动两个服务,一个提供http请求处理添加删除操作,一个启动自定义进程来实现定时任务的执行,在内部http和自定义进程通讯来进行数据交互
但是原生的Crontab定时任务是阻塞执行的,如果有一个执行很久会导致剩下的不能再对应时间执行,阻塞期间http服务与自定义进程之间的通讯还会挂起
然后试过使用swoole做事件循环,在new Crontab的callback中使用go(function(){}),这样确实可以实现不阻塞的执行定时程序,也不会阻塞自定义进程的通讯服务,但是随着定时程序的数量、耗时增多,后面查看日志会发现时不时的exit with status 11、exit with status 65280报错,然后进程自动重启,在runtimes下面也看不到具体的报错日志
截图

后面又试过直接使用swoole开启tcp服务,在workerStart里面初始化定时器,在receive里面接受http的请求来管理定时器执行,因为swoole不支持linux的crontab表达式,webman的实现是生成一个回调函数,拿到下一分钟要执行的定时任务依次Timer::add生成定时器,我把Timer::add替换成\Swoole\Timer::after就可以在swoole中使用crontab表达式,执行的时候也是Co::create()执行的,然后发现定时器可以正常按时间执行,但是定时器执行后tcp服务就被挂起了,无法和http交互
截图

请问各位大佬有类似实现过对应功能吗,怎么解决支持秒级的定时器执行,并且可以进行通讯、定时任务执行期间不阻塞服务的

725 5 0
5个回答

six

架构一般是一个定时进程,一堆http业务进程。
定时进程负责定时触发任务,使用workerman/http-client异步调用,这样定时进程不会有任何阻塞操作。
http业务进程负责处理具体的定时任务,任务本身是否阻塞没有太大影响,只要http进程足够就行。

  • 暂无评论
小Z先生

可以考虑使用队列进行业务逻辑处理,定时器只用于发送队列消息,这样就不需要考虑定时器的阻塞问题了

  • 暂无评论
软饭工程师

使用php协程,参考文档
https://www.workerman.net/a/1723
https://ripple.cloudtay.com/docs/basic/defer/
在定时器里面用协程执行任务,定时器会立即返回,开始执行下一个定时器,并不会阻塞,这样解决了多个定时器互相阻塞的问题

  • latin 2024-09-09

    协程还是当前进程执行,定时任务里如果是阻塞调用,整体进程还是阻塞的。这样就协程没有作用

  • 胡桃 2024-09-09

    Fiber/Revolt 那一套遇到死循环确实只能傻眼,但是 Swoole 有抢占式调度。

  • lcmg 2024-09-09

    后续还是用swoole实现了,swoole开启的tcp服务设置运行协程和一键协程化之后,tcp的通讯服务和定时任务就互不阻塞了。其中最主要的问题还是用swoole作为webman的eventloop之后,看起来是互相冲突导致webman子进程重启,没这个问题的话两个一起用还是挺方便的
    $server = new Server($host, (int)$port, SWOOLE_PROCESS);
    // 设置服务器为异步非阻塞模式
    $server->set([
    'worker_num' => 1,
    'enable_coroutine' => true,
    'max_coroutine' => 3000,
    'daemonize' => true,
    'log_file' => runtime_path() . '/logs/swoole.log',
    'log_date_format' => '%Y-%m-%d %H:%M:%S',
    'log_rotation' => SWOOLE_LOG_ROTATION_DAILY,
    'display_errors' => true,
    'hook_flags' => SWOOLE_HOOK_ALL,
    ]);
    // 当有客户端数据到达时
    $server->on('receive', function (Server $server, $fd, $reactor_id, $data) {
    Co::create(function () use ($server, $fd, $data) {
    // echo "Received data from client {$fd}: {$data}\n";
    try {
    $data = json_decode($data, true);
    $method = $data['method']?:'';
    $args = $data['args']?:[];
    $res = $this->onReceive($method,$args);
    $server->send($fd,$res);
    }catch (\Exception $exception){
    $server->send($fd,json_encode(['code' => 0, 'msg' => $exception->getMessage() ]));
    }
    });
    });

efnic

插件市场:https://www.workerman.net/app/view/cron
最关键的逻辑是使用composer require symfony/process

  • 冰冰不要 2024-09-18

    这是直接一个定时器跑了一个进程吗?

  • efnic 2024-09-21

    执行的时候使用proc_open 执行一个命令,并且打开用来输入/输出的文件指针。

jcy_tcp

你应该已经完成。看到这个想到上家公司自己做的定时调度,感觉也挺好。用ai把我记忆中上家公司的实现流程总结出来了。供参考:

你的方案是可行的,尤其是结合了多进程、定时调度、任务状态管理以及异步执行的设计,符合大多数任务调度系统的最佳实践。以下是对你的方案的总结和优化建议:

方案总结

1. 任务调度表设计

  • 你会维护一个数据库表来记录任务,每个任务包括:
    • 任务 ID任务名称Crontab 表达式(用于控制任务的执行时间)、执行模式(单次、多次、循环)、任务状态(待执行、执行中、成功、失败、超时)、任务超时时间日志关联上次执行时间下次执行时间等。
  • 任务表会定期扫描以触发符合条件的任务,保证在合适的时间点执行。

2. 多进程调度

  • 使用 多进程(或进程池) 来处理多个任务的并发执行。通过 symfony/process 启动多个子进程,让每个任务在独立的进程中执行,避免任务之间的相互阻塞或影响。
  • 对于并发量较大的场景,你可以通过 进程池 来控制并发量,保证资源的合理分配。

3. Crontab 表达式解析

  • 你计划使用 composer 包(例如 mtdowling/cron-expression)来解析任务的 crontab 表达式,从而决定任务的下次执行时间。这可以保证灵活的任务调度。

4. 协程与异步执行

  • 使用 swoole 提供的协程来进一步优化任务的异步执行,尤其是涉及网络请求、数据库 I/O 等操作时,协程能有效提升任务的执行效率,避免阻塞。
  • 你也可以通过消息队列(如 RabbitMQRedis)进一步解耦任务调度和执行,确保任务的异步处理和高效并发。

5. 任务锁与并发控制

  • 在多进程并发执行时,需要确保每个任务只会被一个进程获取和执行。你计划使用锁机制来防止重复执行任务。
    • 推荐使用 Redis 分布式锁:通过 SETNX 来加锁任务,确保每个任务在同一时刻只会被一个进程处理。任务执行完毕后,释放 Redis 锁。
    • 你还可以选择使用数据库行锁(SELECT ... FOR UPDATE)或文件锁(flock)来加锁任务。

6. 任务超时与状态管理

  • 每个任务有明确的执行超时时间,使用 symfony/processswoole 的超时机制确保任务不会长时间占用资源,任务执行时间超过设定的超时时间后自动终止,并标记为“超时”状态。
  • 根据任务的执行结果,更新任务状态为 成功失败超时,并在多次或循环任务的情况下恢复状态为 待执行

7. 任务日志记录

  • 每个任务执行过程中的输出、执行时间、状态、错误信息等都需要记录到日志中,以便追踪和排查问题。日志表记录可以与任务表建立关联。

优化建议

  1. 任务锁定的策略

    • 建议使用 Redis 分布式锁 实现任务锁定,Redis 锁更轻量级且能有效解决多进程下的任务竞争问题。确保锁的过期时间适当设置,以防止死锁。
  2. 负载均衡与扩展

    • 当任务量特别大时,可以考虑使用 消息队列 来分发任务,并通过 分布式部署 的方式扩展任务处理能力,避免单一进程成为瓶颈。
  3. 监控与告警

    • 可以集成监控工具(如 PrometheusGrafana)来监控任务的执行状态与性能,尤其在任务超时、失败等异常情况下,触发告警通知以便及时处理。

总结

总体来说,你的方案充分考虑了定时调度、任务并发、异步执行、任务状态管理和日志记录,具备较强的扩展性和鲁棒性。通过适当的锁机制、进程管理与协程结合,能够实现一个高效、稳定的任务调度系统。

  • 暂无评论
×
🔝