THINKPHP6 佇列

learning_php發表於2020-09-29

1.安裝think-queue

composer require topthink/think-queue

2.配置訊息佇列,將config/queue.php將’default’ => ‘sync’改為’default’ => ‘redis’,使用Redis驅動

如選擇database,需建立表

CREATE TABLE `prefix_jobs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `queue` varchar(255) NOT NULL, `payload` longtext NOT NULL, `attempts` tinyint(3) unsigned NOT NULL, `reserve_time` int(10) unsigned DEFAULT NULL, `available_time` int(10) unsigned NOT NULL, `create_time` int(10) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

3.建立生產者

class Index extends BaseController
{
    
    /**
     * 單任務
     */
    public function singleTask()
    {
        //當前任務將由哪個類來負責處理
        $jobHandlerClassName = 'app\job\Job1';
        //業務資料 物件需要手動轉序列化
        $jobData = ['ts' => time()];
        //佇列名稱
        $jobQueueName = "createOrderJob";
        //入佇列,later延時傳送,單位秒。push立即傳送
        $isPushed = Queue::later(2, $jobHandlerClassName, $jobData,$jobQueueName);
        //$isPushed = Queue::push( $jobHandlerClassName , $jobData , $jobQueueName );
        // database 驅動時,返回值為 1|false  ;   redis 驅動時,返回值為 隨機字串|false
        if( $isPushed !== false ){
            echo '執行成功';
        }else{
            echo '執行失敗';
        }
        //php think queue:listen --queue createOrderJob  執行佇列
        //nohup php think queue:listen --queue createOrderJob &  不以守護程式執行
    }

    /**
     * 多工
     */
    public function multiTask(){
        $taskType = $_GET['taskType'];
        switch ($taskType) {
            case 'taskA':
                $jobHandlerClassName  = 'app\job\MultiTask@taskA';
                $jobDataArr = ['a'   => '1'];
                $jobQueueName = "multiTaskJobQueue";
                break;
            case 'taskB':
                $jobHandlerClassName  = 'app\job\MultiTask@taskB';
                $jobDataArr = ['b'   => '2'];
                $jobQueueName = "multiTaskJobQueue";
                break;
            default:
                break;
        }

        $isPushed = Queue::push($jobHandlerClassName, $jobDataArr, $jobQueueName);
        if ($isPushed !== false) {
            echo("the $taskType of MultiTask Job has been Pushed to ".$jobQueueName ."<br>");
        }else{
            echo "push a new $taskType of MultiTask Job Failed!";
        }
    }

}

4.建立消費者

單任務

class Job1
{
    public function fire(Job $job, $data)
    {
        //業務處理程式碼,具體不貼出來了
        $isJobDone = $this->jobDone($data);
        //執行成功刪除
        if($isJobDone){
            $job->delete();
            print("任務已經被執行成功並且刪除");
        }else{
            $job->release(3); //$delay為延遲時間 表示該任務延遲3秒後再執行
            print("任務3s後再次被執行");
        }
        //通過這個方法可以檢查任務重試了幾次
        if ($job->attempts() > 3) {
            print("Job has been retried more than 3 times!");
            $job->delete();
        }
    }

    public function failed($data)
    {
        // ...任務達到最大重試次數後,失敗了
    }

    private function jobDone($data){
        Log::write('這是資料 ' . json_encode($data));
        return true;
    }

多工

class MultiTask{
    public function taskA(Job $job,$data){

        $isJobDone = $this->_doTaskA($data);

        if ($isJobDone) {
            $job->delete();
            print("Info: TaskA of Job MultiTask has been done and deleted"."\n");
        }else{
            $job->release(3);
            print("任務3s後再次被執行");
        }
        if ($job->attempts() > 3) {
            print("Job has been retried more than 3 times!");
            $job->delete();
        }
    }

    public function taskB(Job $job,$data){

        $isJobDone = $this->_doTaskB($data);

        if ($isJobDone) {
            $job->delete();
            print("Info: TaskB of Job MultiTask has been done and deleted"."\n");
        }else{
            $job->release(3);
            print("任務3s後再次被執行");
        }
        if ($job->attempts() > 3) {
            print("Job has been retried more than 3 times!");
            $job->delete();
        }
    }

    private function _doTaskA($data) {
        print("Info: doing TaskA of Job MultiTask "."\n");
        return true;
    }

    private function _doTaskB($data) {
        print("Info: doing TaskB of Job MultiTask "."\n");
        return true;
    }
}

5.執行

php think queue:listen --queue createOrderJob

我的檔案目錄

在database 模式下,2.7.1 和 2.7.2 中的重發邏輯是先刪除原來的任務,然後插入一個新的任務。2.7.3 中的重發時機是直接更新原任務。

而在redis 模式下,3種重發都是先刪除再插入。

不管是哪種重發方式,重發之後,任務的已嘗試次數會在原來的基礎上 +1 。

此外,消費者類中需要注意,如果 fire() 方法中可能丟擲異常,那麼如果不需要自動重發的話, 請在丟擲異常之前將任務刪除 $job->delete() ,以免產生bug。 如果需要自動重發的話,請直接丟擲異常,不要在 fire() 方法中又手動使用 $job->release() , 這樣會導致該任務被重發兩次,產生兩個一樣的新任務。

相關文章