Autotask.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. <?php
  2. namespace addons\crontab\controller;
  3. use addons\crontab\model\Crontab;
  4. use Cron\CronExpression;
  5. use fast\Http;
  6. use think\Controller;
  7. use think\Db;
  8. use think\Exception;
  9. use think\Log;
  10. /**
  11. * 定时任务接口
  12. *
  13. * 以Crontab方式每分钟定时执行,且只可以Cli方式运行
  14. * @internal
  15. */
  16. class Autotask extends Controller
  17. {
  18. /**
  19. * 初始化方法,最前且始终执行
  20. */
  21. public function _initialize()
  22. {
  23. // 只可以以cli方式执行
  24. if (!$this->request->isCli()) {
  25. $this->error('Autotask script only work at client!');
  26. }
  27. parent::_initialize();
  28. // 清除错误
  29. error_reporting(0);
  30. // 设置永不超时
  31. set_time_limit(0);
  32. }
  33. /**
  34. * 执行定时任务
  35. */
  36. public function index()
  37. {
  38. $withPcntl = false;
  39. $pool = null;
  40. $config = get_addon_config('crontab');
  41. $mode = $config['mode'] ?? 'pcntl';
  42. if ($mode == 'pcntl' && function_exists('pcntl_fork')) {
  43. $withPcntl = true;
  44. $pool = new \Jenner\SimpleFork\Pool();
  45. }
  46. $time = time();
  47. $logDir = LOG_PATH . 'crontab' . DS;
  48. if (!is_dir($logDir)) {
  49. mkdir($logDir, 0755);
  50. }
  51. //筛选未过期且未完成的任务
  52. $crontabList = Crontab::where('status', '=', 'normal')->order('weigh DESC,id DESC')->select();
  53. $execTime = time();
  54. foreach ($crontabList as $crontab) {
  55. $update = [];
  56. $execute = false;
  57. if ($time < $crontab['begintime']) {
  58. //任务未开始
  59. continue;
  60. }
  61. if ($crontab['maximums'] && $crontab['executes'] > $crontab['maximums']) {
  62. //任务已超过最大执行次数
  63. $update['status'] = 'completed';
  64. } else {
  65. if ($crontab['endtime'] > 0 && $time > $crontab['endtime']) {
  66. //任务已过期
  67. $update['status'] = 'expired';
  68. } else {
  69. //重复执行
  70. //如果未到执行时间则继续循环
  71. $cron = CronExpression::factory($crontab['schedule']);
  72. if (!$cron->isDue() || ($crontab['executetime'] && date("YmdHi", $execTime) === date("YmdHi", $crontab['executetime']))) {
  73. continue;
  74. }
  75. $execute = true;
  76. }
  77. }
  78. // 如果允许执行
  79. if ($execute) {
  80. $update['executetime'] = $time;
  81. $update['executes'] = $crontab['executes'] + 1;
  82. $update['status'] = ($crontab['maximums'] > 0 && $update['executes'] >= $crontab['maximums']) ? 'completed' : 'normal';
  83. }
  84. // 如果需要更新状态
  85. if (!$update) {
  86. continue;
  87. }
  88. // 更新状态
  89. $crontab->save($update);
  90. Db::connect()->close();
  91. // 将执行放在后面是为了避免超时导致多次执行
  92. if (!$execute) {
  93. continue;
  94. }
  95. $runnable = new \addons\crontab\library\CommandRunnable($crontab);
  96. if ($withPcntl) {
  97. $process = new \Jenner\SimpleFork\Process($runnable);
  98. $name = $crontab['title'];
  99. $pool->execute($process);
  100. } else {
  101. $runnable->run();
  102. }
  103. }
  104. if ($withPcntl && $pool) {
  105. $pool->wait();
  106. }
  107. return "Execute completed!\n";
  108. }
  109. }