04月06, 2015

后台任务和PHP-Resque的使用(五) 创建任务

到目前为止已经让 Worker 运行了,我们需要创建并添加任务。这一节主要了解什么是任务(Job),以及如何使用任务。

简单的说,任务就是传递给 Worker 要执行的内容。我们需要把 Job 依次添加到 Queue 来执行。

要把任务添加到队列,程序必须要包含 php-resque 库以及 Redis。

使用 require_once '/path/to/php-resque/lib/Resque.php';包含 php-resque 的库文件,它会自动连接到 Redis 服务器,如果你的 Redis 服务器不是默认的localhost:6379,你需要使用Resque::setBackent('192.168.1.56:3680'); 这样的格式来设置你的 Redis 服务器的地址,同样 setBackent 支持可选的第二个参数为使用的 Redis 数据库名,默认为 0 。

现在 php-resque 已经准备好了,使用以下代码添加一个任务到队列:

Resque::enqueue('default', 'Mail', array('dest@mail.com', 'hi!', 'this is a test content'));
  • 第一个参数,'default'是指队列的名字,示例中将会把任务推送到名为 default 的队列中
  • 第二个参数是 Job 的类名,表示要执行哪个 Job
  • 第三个参数是要发送给 Job 的参数也可以使用关联数组的形式

传递给 Job 的参数(上面第三个参数)可以是普通数组、关联数组的形式,也可以是一个字符串,但使用数组可以很方便的传递更多的信息给 Job。所有的参数在推送到队列前都会经过 json_encode 处理。

创建一个 Job

如上面的例子中,第一个参数是队列的名字(还记得上一节里面启动 php resque.php 时传递的 QUEUE 环境变量吗?),第二个参数是 Job 的类名,即要执行的 Job。Mail 类就是一个 Job 类。

所有的Job类都应该包含一个 perform() 方法,使用 Resque::enqueue() 传递的第三个参数可以在 perform() 方法中使用 $this->args 来得到。一个典型的 Job 类如下所示:

class Mail{
    public function perform(){
        var_dump($this->args);
    }
}

Job 类也可以包含 setUp()tearDown() 方法,可选的这两个方法分别会在 perform() 方法之前和之后运行。

class Mail{
    public function setUp(){
        # 这个方法会在perform()之前运行,可以用来做一些初始化工作
        # 如连接数据库、处理参数等
    }

    public function perform(){
        # 执行Job
    }

    public function tearDown(){
        # 会在perform()之后运行,可以用来做一些清理工作
    }
}

包含 Job 类

在实例化 Job 类之前,必须让 Worker 找到并包含这个类。有很多种方法可以做到。

使用 include_path

当 PHP 运行于 Apache model 方式的时候可以使用 .htaccess 设置包含:

php_value include_path ".:/already/existing/path:/path/to/job-classes"

或者通过 php.ini

include_path = ".:/php/includes:/path/to/job-classes"

使用 APP_INCLUDE 包含

上一节说了使用 APP_INCLUDE 指定 Worker 执行时要包含的PHP文件的路径,如:

QUEUE=default APP_INCLUDE=/path/to/loader.php php resque.php

loader.php 的内容可以是下面的那样:

include '/path/to/Mail.php';
include '/path/to/AnotherJobClass.php';
include '/path/to/somewhere/AnotherJobClass.php';
include '/JobClass.php';

当然也可以使用 PHP 的 autoloader 方法 —— sql_autoloader

在你的项目中使用后台任务

以下面的代码为例,把耗时较多的工作交给后台任务来做。

class User{
    # functions(){}  // 其它函数

    public function updateLocation($location) {
        $db->updateUserTable($this->userId, 'location', $location);
        $this->recomputeNewFriends(); # 此操作耗时较长
    }

    public function recomputeNewFriends() {
        # 查找新的朋友
    }
}

把以上代码改成:

class User {
    # functions(){}  // 其它函数

    public function updateLocation($location) {
        $db->updateUserTable($this->userId, 'location', $location);
        # 把任务添加到队列
        # 这里的队列名为 'queueName'
        # 任务名为 'FriendRecommendator'
        Resque::enqueue('queueName', 'FriendRecommendator', array('id' => $this->userId));
    }
}

以下是任务 FriendRecommendator 类的实现代码:

class FriendRecommendator {
    function perform() {
        # 这里没有User类,需要创建一个User类对象
        $user = new User($this->args['id']);
        # 查找新朋友的操作
    }
}

简单的说,你只需要把你的执行任务的代码放到 Job 类中并改名为perform()即可,只要你愿意甚至可以将普通类改成 Job 类,但并不推荐这样做。

perform() 方法有个缺点,即一个 Job 类只能包含一个 perform() 方法,也就是说一个 Job 类只能执行一种后台任务。例如你有一个发送通知信息的后台任务,但又有发送给用户和发送给管理员两个不同的需求,一般来说就得需要两个 Job 类才能实现。不过这里有个小小的 Hack 可以使一个 Job 能执行多个类型的任务。

首先就是给你的 Job 分类,把相似工作的 Job 放在同一个 Job 类中,因为完全不相关的 Job 即使放在同一个类中也没有任何意义。然后通过给 Resque::enqueue() 方法传递一个表示不同 Job 的参数过去。

# Job类中的写法
class Notification{
    function sentToUser(){
        # Code..
    }

    function sentToAdmin{
        # code..
    }

    function perform(){
        $action = $this->{array_shift($this->args)};
        if(method_exists($this, $action)){
            $this->$action();
        }
    }
}

# 添加任务时的写法
Resque::enqueue('default', 'Notification', array('sendToAdmin', 'this is content'));

也可以使用其它类继承 Job 类以获取相同的 perform() 方法,但要注意必须同时包含这些类文件。

另外需要注意的是使用这种 Hack 的方法 Resque::enqueue() 的第三个参数必须是一个数组,并且它的第一个元素是要执行的任务的方法名,并且这个元素会在执行时从 $args 数组中移除。

必须在每次修改 Job 类后重新启动你的Worker


本文由冰翼翻译自Kamisama.me

本文链接:https://icewing.cc/post/background-jobs-and-phpresque-5.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。