Queue.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. <?php
  2. namespace app\command\queue;
  3. use app\command\WorkerCommand;
  4. use app\model\sys\SysSchedule;
  5. use app\service\core\addon\CoreAddonService;
  6. use think\console\Command;
  7. use think\console\Input;
  8. use think\console\input\Argument;
  9. use think\console\input\Option;
  10. use think\console\Output;
  11. use think\facade\Log;
  12. use Workerman\RedisQueue\Client;
  13. use Workerman\Timer;
  14. use Workerman\Worker;
  15. class Queue extends Command
  16. {
  17. use WorkerCommand;
  18. public function configure()
  19. {// 指令配置
  20. $this->setName('queue:listen')
  21. ->addArgument('action', Argument::OPTIONAL, "start|stop|restart|reload|status|connections", 'start')
  22. ->addOption('mode', 'm', Option::VALUE_OPTIONAL, 'Run the workerman server in daemon mode.')
  23. ->setDescription('基于Redis的消息队列,支持消息延迟处理。');
  24. }
  25. /**
  26. * 执行任务
  27. * @return void
  28. */
  29. protected function execute(Input $input, Output $output)
  30. {
  31. $this->resetCli($input, $output);
  32. Worker::$pidFile = runtime_path() . 'workerman_queue.pid';
  33. Worker::$logFile = runtime_path() . 'workerman.log';
  34. $worker = new Worker();
  35. $worker->name = 'queue_work';
  36. // $worker->count = 3;
  37. $worker->onWorkerStart = function() use ($output) {
  38. // 定时,每10秒一次
  39. Timer::add(30, function() use ($output) {
  40. ( new SysSchedule() )->select();
  41. });
  42. $redis_option = [
  43. 'connect_timeout' => 10,
  44. 'max_attempts' => 3,
  45. 'retry_seconds' => 5,
  46. 'prefix' => md5(root_path())
  47. ];
  48. if (!empty(env('redis.redis_password'))) {
  49. $redis_option[ 'auth' ] = env('redis.redis_password');
  50. }
  51. $redis_option[ 'db' ] = env('redis.select');
  52. $client = new Client('redis://' . env('redis.redis_hostname') . ':' . env('redis.port'), $redis_option);
  53. $queue_list = $this->getAllQueue();
  54. foreach ($queue_list as $queue_class_name) {
  55. $queue_class_name = str_replace('.php', '', $queue_class_name);
  56. // 订阅
  57. $client->subscribe($queue_class_name, function($data) use ($queue_class_name, $output) {
  58. echo "\n" . '[' . date('Y-m-d H:i:s') . ']' . " Processing:" . $queue_class_name;
  59. try {
  60. $class_name = '\\' . $queue_class_name;
  61. $class = new $class_name();
  62. $class->fire($data);
  63. } catch (\Throwable $e) {
  64. Log::write(date('Y-m-d H:i:s') . ',队列有错误:' . $queue_class_name . '_' . $e->getMessage() . '_' . $e->getFile() . '_' . $e->getLine());
  65. }
  66. echo "\n" . '[' . date('Y-m-d H:i:s') . ']' . " Processed:" . $queue_class_name;
  67. });
  68. }
  69. // 消费失败触发的回调(可选)
  70. $client->onConsumeFailure(function(\Throwable $exception, $package) use ($output) {
  71. echo "\n" . "队列 " . $package[ 'queue' ] . " 消费失败," . $exception->getMessage();
  72. });
  73. };
  74. Worker::runAll();
  75. }
  76. /**
  77. * 捕获所有队列任务
  78. * @return array
  79. */
  80. public function getAllQueue()
  81. {
  82. $class_list = [];
  83. $system_dir = root_path() . 'app' . DIRECTORY_SEPARATOR . 'job';
  84. $addon_dir = root_path() . 'addon' . DIRECTORY_SEPARATOR;
  85. if (is_dir($system_dir)) {
  86. search_dir($system_dir, $app_data, root_path());
  87. $class_list = array_merge($class_list, $app_data);
  88. }
  89. $addons = ( new CoreAddonService() )->getInstallAddonList();
  90. foreach ($addons as $v) {
  91. $addon_path = $addon_dir . $v[ 'key' ] . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'job';
  92. if (is_dir($addon_path)) {
  93. search_dir($addon_path, $addon_data, root_path());
  94. $class_list = array_merge($class_list, $addon_data);
  95. }
  96. }
  97. foreach ($class_list as &$v) {
  98. $v = str_replace('.php', '', $v);
  99. $v = str_replace('/', '\\', $v);
  100. }
  101. return $class_list;
  102. }
  103. }