laravel 订单通知队列

2017 年 3 月 7 日PHP Standard

需求

订单完成支付后通知服务器已到账,通知失败则重试,最多3次,第二次5秒后,第三次10秒后

实现方案一

生成任务类

php artisan make:job PaymentNotify

命令将会在app/Jobs目录下生成一个新的类,编辑:

< ?php

namespace App\Jobs;

use App\Exceptions\SignException;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class PaymentNotify implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    protected $data;

    /**
     * Create a new job instance.
     *
     * @param $data
     */

    public function __construct($data)
    {
        $this->data = $data;
    }

    /**
     * Execute the job.
     *
     * @throws \Exception
     */
    public function handle()
    {
        if ($this->attempts() == 1) {
            //处理订单通知
            info('notify'.$this->attempts());
            if(true){//测试强制通知失败
                $this->release(5);//手动释放任务回队列,带延时执行时间
            }
        }

        if ($this->attempts() == 2) {
            //处理订单通知
            info('notify'.$this->attempts());
            if(true){
                $this->release(10);
            }
        }

        if ($this->attempts() == 3) {
            //处理订单通知
            info('notify'.$this->attempts());
            if(true){
                throw new SignException('fails');//抛出异常,任务失败,自动入库
            }

        }
    }

}

添加测试路由并访问:

Route::get('/pay/notify', function(){
    dispatch((new \App\Jobs\PaymentNotify(['order'=>time().mt_rand(1000,9999)]))->onQueue('PaymentNotify'));
    return 'ok';
});

监听队列

php artisan queue:work  --queue=PaymentNotify --tries=3  --sleep=0

可查看使用说明

$php artisan queue:work --help
Usage:
  queue:work [options] [--] [<connection>]

Arguments:
  connection               The name of connection

Options:
      --queue[=QUEUE]      The queue to listen on
      --daemon             Run the worker in daemon mode (Deprecated)
      --once               Only process the next job on the queue
      --delay[=DELAY]      Amount of time to delay failed jobs [default: "0"]
      --force              Force the worker to run even in maintenance mode
      --memory[=MEMORY]    The memory limit in megabytes [default: "128"]
      --sleep[=SLEEP]      Number of seconds to sleep when no job is available [default: "3"]
      --timeout[=TIMEOUT]  The number of seconds a child process can run [default: "60"]
      --tries[=TRIES]      Number of times to attempt a job before logging it failed [default: "0"]
  -h, --help               Display this help message
  -q, --quiet              Do not output any message
  -V, --version            Display this application version
      --ansi               Force ANSI output
      --no-ansi            Disable ANSI output
  -n, --no-interaction     Do not ask any interactive question
      --env[=ENV]          The environment the command should run under
  -v|vv|vvv, --verbose     Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

命令意思是:使用默认队列连接的PaymentNotify队列,失败最多尝试执行3次,轮询新任务之前的等待时间为0(即有任务随时检测执行,不会在无任务时默认等待3秒)

任务执行情况

[2017-03-07 16:22:44] local.INFO: notify1  
[2017-03-07 16:22:49] local.INFO: notify2  
[2017-03-07 16:22:59] local.INFO: notify3

符合预期

方案二

另一种思路是订单完成后直接生成3个队列,

$order = time().mt_rand(1000,9999);

dispatch((new \App\Jobs\PaymentNotify(['order'=>$order]))->onQueue('PaymentNotify'));

dispatch((new \App\Jobs\PaymentNotify(['order'=>$order]))->onQueue('PaymentNotify')->delay(5));

dispatch((new \App\Jobs\PaymentNotify(['order'=>$order]))->onQueue('PaymentNotify')->delay(10));

后两个队列带延时执行参数,设置最多执行次数为1,执行队列时检测已经通知成功则跳过执行

总结

方案一:在处理队列时需判断重试次数,分类处理,业务需求变化需改动多;
方案二:在redis中相同队列存在多条,业务需求变化处理逻辑相对改动少;
各有利弊。