Drupal 7 队列 API
Drupal 7 Queues API 是 Drupal 的一些特性,它提供了先进先出的数据结构,由 Drupal 本身在内部使用,并且可以完全定制。
Queues API 不仅仅是 Drupal 代码库的一部分,它在内部用作多个不同进程的一部分。Batch API 允许一次完成很多事情,它建立在 Queues API 之上,并提供了一些自定义的队列类。我不会在这篇文章中介绍批处理 API,但我可能会在以后的文章中介绍它。
Drupal 7 中的新 cron 系统大量使用了 Queues API。它的工作原理是允许其他模块在正常hook_cron()调用期间创建队列项,然后再运行。每个想要在系统 cron 队列中包含一个项目的模块都可以通过实现一个名为 的钩子来实现,该钩子hook_cron_queue_info()告诉 cron 在检索队列项目时回调什么函数。
例如,聚合器模块通过在 cron 函数调用期间创建包含名称为 aggregator_feeds 的提要信息的队列项,使用 cron 更新提要。然后从队列中检索这些项目,并通过调用该aggregator_refresh()函数并传递队列数据中包含的信息来执行这些项目。该aggregator_refresh()函数在中定义aggregator_cron_queue_info()并用于获取新的提要信息。
更新模块还利用了队列 API,它用于存储请求以获取可用更新数据的任务。这些任务随后会从队列中检索并运行。
API 使用面向对象的原则来生成和定义队列,因此系统是完全可定制的。
为什么要使用队列?
在深入了解如何使用和自定义队列之前,我认为有必要了解一些有关如何使用队列来完成某些任务的真实示例。
批处理
如果您正在编写的模块需要一次处理多个项目,而这些项目可能不是来自同一个函数调用,那么将它们添加到队列中可能是有意义的。然后可以在一个地方执行这些项目。
例如,假设当您插入一个节点时,您希望创建多个充当内页的其他节点。然后,您将设置一个队列来添加这些页面并添加所需的菜单项。
项目的顺序处理 在
插入分类术语时,如果您想保持术语之间的父子关系,您通常希望在其他术语之前插入某些术语。通过使用队列,您可以确保一个接一个地处理所有项目,以便在创建子元素之前首先插入任何具有子元素的术语。
延迟处理
如果您有大量数据要处理,那么一次性处理可能会导致系统过载。因此,为了分散这些计算的负载,您可以将它们添加到队列中,然后缓慢地或在系统可用时处理它们。
这方面的一个例子可能是在创建工资系统或类似系统时。您需要确保每个计算都已完成,因此将要计算的项目添加到队列中意味着系统负载将保持在合理水平。这也意味着,如果系统确实出现故障,计算仍将在队列中等待处理。
导入
导入内容是许多 Drupal 程序员的常见任务,有时将节点所需的所有数据放在一起可能是一个昂贵的过程。通过确定需要插入哪些节点并将它们添加到队列中,您可以确保每个节点都被插入而不会使系统过载。
我遇到过一些需要插入大量节点并使用计数系统来跟踪系统在导入过程中的位置的情况。有了队列,我就不需要编写此代码,因为我可以确定添加到队列中的所有项目都会在我要求时检索到。通过这种方式,我可以作为节点的一个子部分进行处理,而不会导致系统崩溃。
防止 API 服务列入黑名单
许多第三方服务(如 Twitter)会跟踪您执行的 API 请求的数量。如果您超过某个限制,他们只会阻止您再做任何事情。如果您有大量项目要处理,那么使用队列来分散这些项目以防止超出 API 配额可能是一个想法。
system.queue.inc 文件
队列 API 位于文件/modules/system/system.queue.inc 中。该文件包含几个接口和一些用于包装队列功能的类。
可靠与不可靠
Drupal 7 定义了两种类型的队列类,您可以使用它们来做不同的事情。这些是可靠的和不可靠的。请注意,该术语是不可靠的,而不是不可靠的。
可靠的
使用可靠的队列,您可以确保每个项目至少执行或处理一次。一个数据库表用于存储队列,这意味着队列可以存在于多个请求中。这也意味着如果请求失败,则队列保持不变。
Drupal 知道对象可靠的唯一方法是它扩展了 DrupalReliableQueueInterface 接口。队列对象本身并不可靠,这意味着它是可靠的。
这是最常用的队列类型,是新队列对象的系统默认值,用途广泛。
不可靠
不可靠队列通常只保存在内存中,因此所有项目可能存在于单个请求中。不能保证队列中的所有项目都将被执行或将按顺序执行。如果请求失败,则队列可能会丢失。
这种队列的一个例子可能是将数据发送到云环境进行处理。当您将数据发送到云环境时,服务器之间通常很少或没有复杂的通信,数据只是被发送。
这种队列的一个例子可能是在创建 Twitter 服务机器人时。如果单个推文失败,通常对用户体验几乎没有影响,因此创建一个不可靠的队列来发送几条推文是一个理想的解决方案。
Drupal 队列类
队列 API 中有三个类提供队列功能。它们是 DrupalQueue、SystemQueue 和 MemoryQueue。
Drupal队列
DrupalQueue 类由一个名为 的静态方法组成get()。要使用它,您需要传递要检索的队列的名称,该方法将返回一个对象,您可以使用该对象与队列进行交互。以下代码是获取队列对象所需的最少代码。
$my_queue = DrupalQueue::get('my_queue');
返回的队列对象将是自定义队列对象或默认系统对象之一,具体取决于已设置的变量。您创建的任何队列都将具有一组独特的项目,这些项目将从您发布的队列名称中挂起。
您可以向该get()方法发送第二个参数,该参数是强制队列对象可靠的布尔值。这里的默认值是 false,意味着该get()方法不会强制返回的队列是可靠的。
对于那些熟悉面向对象编程的人,您可以将 DrupalQueue 视为一个工厂类,因为它的唯一功能是创建队列对象。默认返回的对象是 SystemQueue。
系统队列
SystemQueue 类是 Drupal 中的默认队列类。它是一个可靠队列类的示例,因此实现了 DrupalReliableQueueInterface 接口。该类使用数据库表队列来存储和检索队列。从队列中检索的任何项目都被“租用”以确保没有两个进程获得相同的队列项目。
队列表是在 Drupal 安装过程中创建的,具有以下结构。
柱子 | 类型 | 评论 |
---|---|---|
item_id | int(10) unsigned Auto Increment | 主键:唯一的项目 ID。 |
名称 | varchar(255) | 队列名称。 |
数据 | longblob NULL | 项目的任意数据。 |
到期 | int(11) | 项目的声明租约到期时的时间戳。 |
创建 | int(11) | 创建项目的时间戳。 |
内存队列
MemoryQueue 类是不可靠队列类的一个示例,因此不实现 DrupalReliableQueueInterface 接口。所有队列项都作为类中的 $queue 参数存储在内存中。这个类还实现了物品租赁,以阻止从队列中一遍又一遍地检索相同的物品。
系统变量
DrupalQueue 使用三个系统变量来决定返回什么样的对象,但是设置这些变量可以改变get()方法的输出。这三个变量在此按照它们在get()方法中的使用顺序进行描述。
'queue_class_' 。$name
调用该get()方法时,您可以传入要创建的队列的名称。第一个变量用于查找您创建的任何自定义类。$name 参数用于构造变量名称,其值是您自定义类的名称。如果没有创建该名称的变量,则返回 NULL,在这种情况下不会创建对象。
queue_default_class
如果尚未创建对象,则此变量用于查找要使用的默认系统队列类。这里的默认值是 SystemQueue。
queue_default_reliable_class
如果该get()方法的第二个参数为真,则该get()方法将检查以确保当前创建的对象(如果有)实现 DrupalReliableQueueInterface 接口(即,它是可靠的)。如果不是,则当前创建的对象将被覆盖,以支持默认的可靠类,即 SystemQueue。
该get()方法的默认行为是返回一个 SystemQueue 对象,但这可以通过改变这些变量来改变。
使用 Drupal 队列
使用队列 API 非常简单。您需要做的就是获取您的队列对象,然后您可以使用该对象操作您的队列数据。该createItem()方法可让您将数据添加到队列中,该numberOfItems()方法可让您查看队列中存在的项目数。以下代码获取一个队列,向其中添加一个项目,并检查项目计数。这里添加的项目是一个数组,但这只是为了证明任何东西都可以添加到队列中。MemoryQueue 类将按原样存储项目,而 SystemQueue 类将在将项目存储到数据库之前对其进行序列化。
// 创建队列对象$queue = DrupalQueue::get('my_queue');
// 创建项目
$item = array(
'dataitem1' => 'something',
'int' => 123
);
// 将项目添加到队列
$queue->createItem($item);
// 报告存在的项目数量
echo $queue->numberOfItems(); // 打印“1”"1"
要创建 MemoryQueue 对象,我们必须事先进行一些设置。我们可以将 queue_default_class 变量设置为 MemoryQueue 以便该get()方法将给我们一个 MemoryQueue 对象作为默认值。或者我们也可以设置一个名为 queue_class_memory 的变量,并将值设置为 MemoryQueue。通过这种方式,我们可以获取 MemoryQueue 对象,而无需更改可能会破坏其他模块的系统设置。一旦我们有了 MemoryQueue 对象,它就会以非常相同的方式工作。
// 设置内存队列变量variable_set('queue_class_memory', 'MemoryQueue');
// 获取 MemoryQueue 对象
$queue = DrupalQueue::get('memory');
// 设置项目
$item = array(
'dataitem1' => 'something',
'int' => 123
);
// 将项目添加到队列
$queue->createItem($item);
// 项目数量报告
echo $queue->numberOfItems(); // 打印“1”"1"
要从队列中获取项目,我们使用claimItem()队列对象的方法。这个方法的返回值是一个包含许多属性的对象,重要的是数据属性,它包含我们放入队列的任何内容。
// 获取队列$queue = DrupalQueue::get('my_queue');
// 从队列中声明项目
$got_item = $queue->claimItem();
// 从项目打印数据
echo $got_item->data['dataitem1'];
如果在队列中没有找到项目,则claimItem()返回 FALSE。当您在队列中查找项目时,这是一项重要的检查,因此您应该在尝试使用项目对象之前包括此检查。
// 从队列中声明项目$got_item = $queue->claimItem();
// 确保我们先有一个项目
if ($got_item !== FALSE) {
// 使用物品
echo $got_item>data['dataitem1'];
}
当使用 SystemQueue 类时,返回的 item 对象包含一个 data 和一个 item_id 属性,允许该类稍后唯一标识该项目。所以我们上面检索到的项目如下所示。MemoryQueue 类返回包含更多信息的项目,例如创建时间,否则这些信息将存储在队列表中,并且通常在队列类之外不需要。它保存在 MemoryQueue 项目对象中,因为根本没有其他地方可以放置它。
stdClass Object(
[data] => Array
(
[dataitem1] => something
[qwe] => 123
)
[item_id] => 89
)
该claimItem()方法采用租用时间的可选参数。这是在物品被认领之后,在物品被放回队列之前必须经过的时间量(以秒为单位)。SystemQueue 的默认时间是 24 小时,MemoryQueue 的默认时间是 30 秒。您可以将自己的租用时间传递给此方法,以更改可以再次认领该项目之前经过的时间量。例如,要将租用时间设置为 100 秒,您可以执行以下操作。
$got_item = $queue->claimItem(100);
因此,一旦您拥有队列中的项目,您就可以对它做任何想做的事情。这仅取决于您使用队列的目的。如果您使用队列来存储将用于插入内容的 ID,那么您只需将 ID 传递给您的代码并执行您需要执行的操作。
我认为我使用队列类的最复杂的现实世界示例是为 NWDUG(西北 Drupal 用户组)创建事件联合器。这是几个程序员(包括我自己)之间的合作努力,我们希望使用队列将我们的聚会联合到不同的活动网站。我们存储在队列中的唯一数据是节点 ID 和服务。这意味着我们可以加载最新版本的事件节点并将其传递给我们为该服务创建的发布事件挂钩。这是代码的那部分。
// 声明事件项$event = $queue->claimItem();
if ($event !== FALSE) {
// 如果我们有一个事件,则在子模块中调用 publishevent 钩子
$result = module_invoke($event->data['service'], 'publishevent', node_load($event->data['nid']));
}
释放和删除项目
一旦我们有了我们的队列项并使用了它,我们就可以释放它(重置租用时间)或删除它。要将项目释放回队列,请使用releaseItem()方法,将项目对象作为单个参数传递。
$queue->releaseItem($got_item);要从队列中完全删除项目,请使用deleteItem()方法,将项目对象作为单个参数传递。$queue->deleteItem($got_item);
定制
如果默认系统类不能完全满足您的需要,您可以创建自己的队列类,它可以完全改变 Queues API 的工作方式。创建您自己的队列类时,最好扩展其中一个默认类或实现 DrupalQueueInterface。这将为您提供其他模块开发人员可以使用的通用接口。
如果你的队列类是可靠的,那么它必须实现 DrupalReliableQueueInterface,尽管你也可以扩展 SystemQueue,这几乎是一样的。get()DrupalQueue的方法将检查以确保您的对象在创建可靠队列时实现 DrupalReliableQueueInterface,因此如果您的类不这样做,您将获得 SystemQueue 类。
在您构建了自己的自定义队列类之后,您只需创建一个名为 'queue_class_'的变量即可使其工作。$name,其值是您的队列类的类名。然后,您可以get()使用变量 $name 作为字符串调用 DrupalQueue:: 。设置这个变量通常会在安装钩子或类似的东西中完成。例如,假设我的类名为 MyCustomQueueClass,那么我将使用以下代码将其作为队列对象获取。
// 设置变量(通常在安装钩子中完成)variable_set('queue_class_mycustom', 'MyCustomQueueClass');
// 获取队列 object
$queue = DrupalQueue::get('mycustom');
在我们检索到队列对象后,我们可以像以前一样与它进行交互,实现 DrupalQueueInterface 为我们提供了相同的方法来使用。
自定义队列类示例
我认为创建一些自定义队列类来做不同的事情来演示创建新的队列功能是多么容易,这可能是一个想法,所以他们在这里。
堆栈类
在创建我们自己的队列类时,我们可以做的最简单的事情是用我们自己的功能覆盖默认的队列类。Stack 类是一个栈数据结构的实现,后进先出的实现,使用 SystemQueue 类作为模板。我们从 SystemQueue 类更改的唯一内容是在第一个 SQL 查询中,我们将其更改为按创建降序而不是升序排序。这意味着我们放入对象的最后一个项目是我们返回的第一个项目。
class Stack extends SystemQueue {
public function claimItem($lease_time = 30) {
while (TRUE) {
$item = db_query_range('SELECT data, item_id FROM {queue} q WHERE
expire = 0 AND name = :name ORDER BY created DESC', 0, 1,
array(':name' => $this->name))->fetchObject();
if ($item) {
$update = db_update('queue')
->fields(array(
'expire' => time() + $lease_time,
))
->condition('item_id', $item->item_id)
->condition('expire', 0);
// 如果有受影响的行,则此更新成功。
if ($update->execute()) {
$item->data = unserialize($item->data);
return $item;
}
}
else {
// 目前没有可领取的物品。
return FALSE;
}
}
}
}
WatchdogSystemQueue
此类扩展了 SystemQueue 类,并在每次完成任何操作时创建日志。它还保留了原始的 SystemQueue 功能,因此可能是了解 Drupal 系统内部发生的事情的好方法。您可以以正常方式使用此类,也可以覆盖 queue_default_class 和 queue_default_reliable_class 变量,以便您的 Drupal 安装使用此类作为默认值。
class WatchdogSystemQueue extends SystemQueue {
public function __construct($name) {
watchdog('queue', '%name queue created', array('%name' => $name));
parent::__construct($name);
}
public function createItem($data) {
watchdog('queue', 'Item created : %data', array('%data' => print_r($data, TRUE)));
parent::createItem($data);
}
public function numberOfItems() {
$count = parent::numberOfItems();
watchdog('queue', 'Current item count = %num', array('%num' => $count));
}
public function claimItem($lease_time = 3600) {
$return_value = parent::claimItem($lease_time);
watchdog('queue', 'Item claimed %item (lease time = %lease)', array('%item' => print_r($return_value, TRUE), '%lease' => $lease_time));
return $return_value;
}
public function deleteItem($item) {
watchdog('queue', 'Item deleted : %item', array('%item' => print_r($item, TRUE)));
parent::deleteItem($item);
}
public function releaseItem($item) {
watchdog('queue', 'Item released : %item', array('%item' => print_r($item, TRUE)));
parent::releaseItem($item);
}
public function createQueue() {
watchdog('queue', 'Queue created ');
parent::createQueue();
}
public function deleteQueue() {
watchdog('queue', 'Queue deleted');
parent::deleteQueue();
}
}
EventQueue 类
该类是 NWDUG 网站的一部分所必需的。我们需要的是在发布一个事件之后,在该事件被联合到不同的服务(如 Twitter、EventBrite、Yahoo Upcoming)之前的一段宽限期。实施的是延迟队列,其中任何添加的项目仅在 45 分钟后可用。我们创建的类扩展了 SystemQueue 类,只是以与上面 Stack 类相同的方式覆盖了一些核心功能。
class EventQueue extends SystemQueue {
public function claimItem($lease_time = 30) {
while (TRUE) {
// 防止检索不到 45 分钟(2700 秒)的项目
$item = db_query_range('SELECT data, item_id FROM {queue} q WHERE expire = 0 AND name = :name AND created >= UNIX_TIMESTAMP(DATE_ADD(NOW(), INTERVAL 2700 SECOND)) ORDER BY created ASC', 0, 1, array(':name' => $this->name))->fetchObject();
if ($item) {
$update = db_update('queue')
->fields(array(
'expire' => time() + $lease_time,
))
->condition('item_id', $item->item_id)
->condition('expire', 0);
// 如果有受影响的行,则此更新成功。
if ($update->execute()) {
$item->data = unserialize($item->data);
return $item;
}
}
else {
// 目前没有可领取的物品。
return FALSE;
}
}
}
}
RandomMemoryQueue
RandomMemoryQueue 是一个有点愚蠢的类,但创建它是为了表明可以以与 SystemQueue 类相同的方式扩展 MemoryQueue 类。项目按顺序添加但以随机顺序检索,保持原始类的租用时间系统。
class RandomMemoryQueue extends MemoryQueue {
public function claimItem($lease_time = 30) {
$available_items = array();
// 提取挖掘可用项目
foreach ($this->queue as $key => $item) {
if ($item->expire == 0) {
$available_items[] = $item;
}
}
// 随机选择一个(如果有)
if (count($available_items) > 0) {
$queue_length = count($this->queue);
$rand_item = rand(0, $queue_length - 1);
$item = $available_items[$rand_item];
$item->expire = time() + $lease_time;
return $item;
}
return FALSE;
}
}
创造与毁灭
DrupalQueueInterface 接口有两种方法可用于创建和销毁队列。安装模块时已完成,只需运行一次。您可以通过使用 DrupalQueue::get()方法以与通常相同的方式获取队列对象来调用它们。
createQueue()
用于创建任何自定义队列表或设置变量。应该在安装钩子内调用。
deleteQueue()
用于删除任何自定义队列表并删除任何自定义变量。应在卸载挂钩内调用。
任何核心 Drupal 队列类都不使用这些方法,因为在安装 Drupal 时已经为它们设置了一切。
提示
以下是使用 Drupal Queues API 时可能会派上用场的一些技巧。
除非你真的需要重写整个类,否则最好扩展 SystemQueue 或 MemoryQueue。
对于开源项目中的自定义队列类,尽量保持与默认系统队列相同的检索项目结构。
不要更改系统变量来获取 MemoryQueue 对象,而是使用 queue_class_
参数并将值设置为MemoryQueue。 默认队列类从不检查队列项的唯一性。如果您不小心,完全有可能一遍又一遍地添加相同的项目。
资源
Drupal 队列 API
该 API 在 http://api.drupal.org/api/drupal/modules--system--system.queue.inc/group/queue/7 中有很好的记录
队列 UI 模块
已经创建了一个名为 Queue UI 的模块,它允许您检查队列的内容。
源代码
队列 API 的实际源代码有很好的文档记录,可以在 /modules/system/system.queue.inc 找到
以上是 Drupal 7 队列 API 的全部内容, 来源链接: utcz.com/z/322729.html