Gearman 使用案例(PHP--配合Yii2异步发送Email)

Gearman 使用案例(PHP–Yii2)


本文介绍如何在Yii2框架中利用Gearman进行异步发送电子邮件

过程描述:
  1. 首先安装Gearman,配置Yii2框架中的mailer以及log组件,log组件主要是为了方便输出日志;
  2. 创建gearman-worker,代码示例中GearmanController/actionWorker方法,在项目根目录下面执行 ./yii gearman/worker 即可启动worker;
  3. 创建gearman-client,代码示例中的GearmanController/actionClient方法,由于Gearman协议只支持传递字符串,所以在Client将mailer组件需要的参数配置成数组形式,再利用serialize()序列化,然后在worker中反序列化,执行命令 ./yii gearman/client 启动Client;
  4. 创建保护进程,防止worker被系统杀死,中断任务执行,如GearmanController/actionProtect方法, 执行命令 ./yii gearman/protect 即可启动5个worker,而且在worker被中止以后能够自动重启新的worker;
  5. 如果想防止protect进程也被kill掉,可以配合supervisor等进程管理工具实现对保护进程的管理,想看本人的另外一篇日志;

下面的代码示例部分与我自己的业务逻辑有关,请注意分辨

完整代码如下:

<?php
/**
 * Created by PhpStorm.
 * User: gamelife
 * Date: 2016/3/29
 * Time: 19:21
 */

namespace console\controllers;


use Yii;
use yii\base\Exception;
use yii\console\Controller;
use yii\helpers\Console;

use GearmanWorker;
use GearmanJob;
use GearmanClient;

use common\models\SystemTask;
use common\models\Task;
use common\models\TaskContent;

class GearmanController extends  Controller
{
    const MAX_CHILD_PROCESS_NUM = 5;

    public function beforeAction($action)
    {
        defined('APP_BASE_PATH') or define('APP_BASE_PATH', dirname(dirname(__DIR__)));
        return parent::beforeAction($action);
    }

    public function actionWorker()
    {
        $worker = new GearmanWorker();
        $worker->addServer('127.0.0.1', 4730);

        $worker->addFunction('sendemail', function(GearmanJob $job)
        {
            Yii::$app->mailer->useFileTransport = false;

            Yii::$app->log->targets['file']->logFile =  Yii::$app->getRuntimePath() . '/logs/' .'gearmanlog-' . date("Y_m_d") . '.log';
            Yii::$app->log->targets['file']->logVars = [];

            $emaildetails = unserialize($job->workload());

            $task = Task::findOne($emaildetails['taskid']);
            $systemtask = SystemTask::findOne($emaildetails['systemtaskid']);

            try
            {
               $result = Yii::$app->mailer->compose($emaildetails['compose']['template'], $emaildetails['compose']['template_vars'])
                    ->setTo($emaildetails['setto'])
                    ->setSubject($emaildetails['setsubject'])
                    ->send();

               if ($result)
               {
                    if ($task->use_default_template)
                    {
                        $taskcontent = TaskContent::findOne($emaildetails['taskcontentid']);
                        $taskcontent->used_count += 1;
                        $taskcontent->save();
                    }
                   Yii::error('任务' . $systemtask->id . '执行成功');
                   $systemtask->task_status = SystemTask::STATUS_SUCCESS;
               } else {
                   Yii::error('任务' . $systemtask->id . '执行失败');
                   $systemtask->task_status = SystemTask::STATUS_FAIL;
               }
            } catch (Exception $e)
            {
                $systemtask->task_status = SystemTask::STATUS_FAIL;
                Yii::error('任务' . $systemtask->id . '执行出错');
            }

            $systemtask->running = SystemTask::IS_NOT_RUNNING;
            $task->save();
            $systemtask->save();
        });

        while($worker->work())
        {
            if ($worker->returnCode() !== GEARMAN_SUCCESS)
            {
                echo "Something Wrong" . PHP_EOL;
            }
        }
    }

    public function actionProtect()
    {
        if (extension_loaded('pcntl'))
        {
            declare(ticks=1);
            $child_process_num = 0;

            $signal_handler = function($signal)
            {
                global $child_process_num;
                switch($signal)
                {
                    case SIGCHLD:
                    case SIGINT:
                    case SIGTERM:
                        $child_process_num--;
                        $this->stdout("进程被kill,中断(ctrl+c)或者退出" . PHP_EOL, Console::BOLD);
                        break;
                    default:
                        break;
                }
            };

            pcntl_signal(SIGCHLD, $signal_handler);
            pcntl_signal(SIGINT, $signal_handler);
            pcntl_signal(SIGTERM, $signal_handler);

            while(true)
            {
                $child_process_num++;
                $parentpid = getmypid();
                $forkpid = pcntl_fork();

                if ($forkpid == -1)
                {
                    $this->stdout('创建新的进程失败' . PHP_EOL, Console::BOLD);
                    exit(0);
                } else if ($forkpid > 0)
                {
                    $this->stdout('我是主进程,我的进程id是 ' . $forkpid . ' 我的父进程id是 ' . $parentpid . PHP_EOL, Console::BOLD);
                    if ($child_process_num >= static::MAX_CHILD_PROCESS_NUM)
                    {
                        pcntl_wait($status);
                    }
                } else if ($forkpid == 0)
                {
                    $this->stdout('我是子进程,我的父进程id是  ' . $parentpid." 我的进程id是  " . getmypid() . " ,当前共有{$child_process_num}个进程" . PHP_EOL, Console::BOLD);
                    pcntl_exec('/usr/bin/php', [ APP_BASE_PATH . '/yii', 'gearman/worker']);
                }

                pcntl_signal_dispatch();
                sleep(rand(2,6));
            }

        } else {
            throw  new Exception('Please install PHP pcntl extension!');
        }
    }

    public function actionClient()
    {
        $client = new GearmanClient();
        $client->addServer('127.0.0.1', 4730);

        $emailDetails = [
            'compose' => [
                'template' => Task::DEFAULT_TEMPLATE_NAME,
                'template_vars' => [
                    'lovemessage' => 'Hello World',
                ],
            ],
            'setto' => '',
            'setsubject' => 'Gearman 测试邮件',
            'taskid' => 8,
            'systemtaskid' => 1,
            'taskcontentid' => null,
        ];

        $client->addTaskBackground('sendemail', serialize($emailDetails));

        $client->runTasks();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值