laravel 异步监控定时调度器

定时调度器是什么

laravel默认提供了一个命令定时任务的功能,在其余的php框架下面,没有这个定时任务,咱们要跑一些异步脚本怎么操做呢,只能依赖咱们系统提供的crontab来作,这就致使咱们每次发版本新增定时任务都要去服务器更改crontab代码,获取更新这个配置。php

执行命令是php artisan schedule:run 来执行,那放在哪里执行呢,没错这个调起仍是须要依赖咱们crontab来执行,可是只须要配置一次,后续全部定时任务都在咱们业务代码进行控制linux

场景

咱们有一个导入数据的定时任务laravel

//每分钟导入库数据
$schedule->command(self::SIGNATURE)->withoutOverlapping()->everyMinute()->runInBackground();
复制代码
  1. 这里导入长时间最好使用runInBackground(),表示异步执行,其实就是在shell脚本的末尾加上 & 符号,在linux上彻底依赖系统的方式完成。
  2. 这里使用了withoutOverlapping() 表示在同一时刻只能有一个任务执行,主要逻辑使用的是排它锁实现,依赖于咱们cache的driver,我这里使用的是redis,后面做为锁的过时直接redis提供的key过时来作。

出现问题

这个任务在正常状况下都是很是完美的,由于同一时刻只有一个再跑,跑完就能够,可是一个场景出现git

有一天咱们的qa同窗刚部署环境后,咱们服务端就在默默的导入库了,由于使用withoutOverlapping($expire_at=1440)这个时候在redis就有一个锁产生了,这个默认带参数是锁的过时时间,默认是一天,而后由于咱们docker环境须要更改参数而后进行后端server服务的重启,咱们重启也是比较暴力,就是直接发送kill的信号,致使全部在里面跑的进程瞬间kill,而这时候咱们的redis的锁缺还存在,并且是1440分钟左右,那当咱们server再启动后,发现锁一直存在,没办法进行后续的操做了,只能等着。github

解决

  1. 那我把锁的时间减小行不行,原来1天,我改为30分钟,没问题,开始初版方案咱们也是这样作,官方也是能够这样作的。
  2. 后来咱们一想,可否作到一个监控程序呢,进程退出后立马监控到过时呢,这样就不用固定一个时间,这固然是全部软件开发者理想状态:要你开你就开,我挂了锁也就去掉了,不论正常与否。

解决方案

说明:redis

  1. 这里命令启动时候,获取进程的pid,而后fork子进程,能够将这个pid传递给子进程。
  2. 子进程每隔10s进行一个探活,获取父进程的id与传入的pid是否一致,这里普及一个知识点,若是父进程异常退出,这个子进程未退出就会被init进程(pid=1)接管,那么这就是一个孤儿进程。
  3. 同时子进程每次探活的时候就会更改redis的锁的过时时间,若是探活时间间隔是10s,那么咱们的过时时间设置就是14s,多冗余一点时间。

代码实现

代码实现老是那么苍白无力哈,这里就写一个laravel的扩展来作,好处就是不影响咱们主体的任何代码就完成了,咱们的laravel能够随意升级。docker

github地址:github.com/zzh78727258…shell

composer地址:packagist.org/packages/ze…后端

总结

  1. 总体实现没有使用判断进程是否存在的ps grep等命令,由于咱们docker环境不必定支持这些命令,只是用简单的pid与parent_id作对比。
  2. laravel的在命令开始于结束都进行钩子方式,咱们在Listener下面进行监听便可
public function subscribe($events)
    {
        $events->listen(
            [
                CommandStarting::class, // 命令开始的时候
            ],
            __CLASS__ . '@handle'
        );
    }
复制代码
  1. 总体代码是基于laravel扩展化的,不会影响laravel的升级操做。
相关文章
相关标签/搜索