Drupal 11:创建自定义队列
在 Drupal 开发里,运用核心队列类创建队列相当简便,你只需一个向队列添加数据的机制以及一个处理数据的工作器即可。
由于添加到队列的数据会被序列化,所以几乎能向队列添加任何你想要的数据,唯一的限制在于将数据从队列中取出后进行重新构建。
在某些情形下,需要对 Drupal 核心队列系统做出更改。比如,你可能想要把数据分离到不同的表中,或者采用不同的逻辑来创建或存储队列项,甚至集成第三方队列系统来管理队列。
虽然上述这些示例都具备可行性,但它们要求对队列 API 有一定了解,并且需要额外的服务和设置才能正常运作。
在本文中,我公司将探讨如何创建自定义队列,以及把该队列与 Drupal 集成所需的队列工厂。同时,还会了解一些用于将某些队列替换为自定义队列实现的设置。本文所涉及的所有代码均可在我公司的 GitHub 上的 Drupal 队列示例仓库中找到,特别是 queue_custom_example 模块。
首先,我们来看看在 Drupal 中让队列正常工作需要哪些条件。
一、使用 QueueInterface 接口创建自定义队列
\Drupal\Core\Queue\QueueInterface 接口用于构建管理队列项的框架,你的队列对象必须包含以下方法:
- createItem($data) - 在队列中创建一个项。在 Drupal 核心中,你传递的数据会被序列化,但你可以以任意方式存储数据。
- numberOfItems() - 报告队列中的项数。
- claimItem($lease_time = 3600) - 从队列中认领一个项。$lease_time 变量用于更新队列项,确保在该时间限制内不会再次认领该项。默认值为 3600 秒,也就是 1 小时。
- deleteItem($item) - 从队列中删除一个项。这里的 $item 是 claimItem() 方法的结果,并且应该有一个标识符,用于定位队列项。
- releaseItem($item) - 释放之前认领的队列项。同样,这里的 $item 是 claimItem() 方法的返回值。实际上,这应该将租用时间重置为 0,并且当该项到达队列顶部时会被重新拾取。
- createQueue() - 通过创建自定义队列表或生成队列所需的任何东西来创建队列的存储。目前在 Drupal 核心中未使用此方法,因为内存队列不需要存储,而 DatabaseQueue 会在 createItem() 方法中动态创建表。
- deleteQueue() - 从系统中移除队列的存储。
除非你围绕完全自定义的队列处理程序构建队列,否则最好的做法是扩展现有的核心队列类之一。这样一来,你就无需从头开始实现所有这些方法。
例如,以下的 CustomQueueDatabase 类扩展了核心的 \Drupal\Core\Queue\DatabaseQueue 类,目的是更改正在使用的表的名称。此队列的行为与标准数据库队列相同,但会将数据存储在名为 “queue_custom” 的表中。上述其余的函数通过扩展基类包含在内。
<?php
declare(strict_types=1);
namespace Drupal\queue_custom_example;
use Drupal\Core\Queue\DatabaseQueue;
/**
* 基于核心数据库队列的自定义队列。
*/
class CustomQueueDatabase extends DatabaseQueue {
/**
* 数据库表名。
*/
public const TABLE_NAME = 'queue_custom';
}
之后,我们可以扩展任何函数,从而更改队列底层功能的工作方式。
或者,如果我们想与像 RabbitMQ 这样的队列系统集成,那么就需要从头开始创建一个队列处理程序。该类将与 RabbitMQ 队列系统集成,并允许 Drupal 管理该队列中的项。
二、创建自定义队列工厂
为了使用这个自定义队列,我们需要创建一个队列工厂。这是 Drupal 在内部创建队列处理程序类的方式,也是将自定义队列插入 Drupal 队列生态系统的最简单方法。
首先,我们需要创建一个名为 “queue_custom_example.database” 的命名服务。从 Drupal 11 开始,服务文件中的 “autoconfigure” 选项十分重要,因为它告知 Drupal 父服务也应该包含在这个服务中。
services:
_defaults:
autoconfigure: true
queue_custom_example.database:
class: Drupal\queue_custom_example\CustomQueueDatabaseFactory
parent: queue.database
该服务较为简单,我们已经告知 Drupal 我们将扩展 QueueDatabaseFactory 类,所以在类中也是这么做的。get() 方法需要返回 \Drupal\Core\Queue\QueueInterface 接口的一个实例。这正是我们在创建一个实现此接口的 CustomQueueDatabase 新对象时所做的,这个对象在前面的步骤中已经定义。
<?php
declare(strict_types=1);
namespace Drupal\queue_custom_example;
use Drupal\Core\Queue\QueueDatabaseFactory;
/**
* 用于创建 CustomQueueDatabase 队列的队列工厂类。
*/
class CustomQueueDatabaseFactory extends QueueDatabaseFactory {
/**
* {@inheritdoc}
*/
public function get($name) {
return new CustomQueueDatabase($name, $this->connection);
}
}
QueueDatabaseFactory 类实现了 \Drupal\Core\Queue\QueueFactoryInterface 接口,这是队列工厂的一个要求。如果你不扩展 QueueDatabaseFactory 类,那么你需要实现这个接口,否则 Drupal 会抛出错误。
我们可以通过两种方式使用这个队列。最简单的方法是直接使用我们的自定义队列服务,它将返回一个可用的队列项。
/** @var \Drupal\Core\Queue\QueueInterface $queue */
$queue = \Drupal::service('queue_custom_example.database')->get('queue_custom_example');
或者,我们可以告诉 Drupal,当使用 “queue_custom_example” 时,必须使用特定的服务类来管理队列。我们通过使用 settings.php 文件更改队列来实现这一点。
三、使用设置更改队列
可以在你的 settings.php 文件的 $settings 数组中设置以下值,以定义用于队列的服务:
- queue_reliable_service_$name - 用于可靠队列 “$name” 的容器服务。可靠队列是实现了 \Drupal\Core\Queue\ReliableQueueInterface 接口的任何队列,并且预计在页面加载之间存储值。数据库队列是一个可靠队列,因为它实现了这个接口。
- queue_service_$name - 用于队列 “$name” 的容器服务。
- queue_default - 默认情况下用于无覆盖队列的容器服务。默认值为 'queue.database'。
如前所述,可以将这些设置添加到网站的 settings.php 文件中。例如,要更改用于 queue_custom_example 队列的服务类,我们可以执行以下操作。
$settings['queue_service_queue_custom_example'] = 'queue_custom_example.database';
如果你在安装网站后添加此设置,请确保清除缓存。
现在,你可以使用 Drupal 中的普通 “queue” 服务,但在这种情况下,该设置将导致使用 “queue_custom_example.database” 工厂来加载我们的自定义队列类。
/** @var \Drupal\Core\Queue\QueueInterface $queue */
$queue = \Drupal::service('queue')->get('queue_custom_example');
如果你愿意,可以使用 “queue_default” 设置将网站中的所有队列处理程序更改为指向自定义服务。建议不要更改核心队列处理程序,只更改队列。
注意,此功能原计划被弃用,但由于替换功能准备延迟,至少在 11.1.0 版本之前不会被弃用。目前尚未确定替换功能,所以我认为在这里记录是安全的。
四、结论
Drupal 中的队列系统具备高度的模块化,并且可以根据需求进行替换。Drupal 中的默认队列类工作得相当出色,但当你想要扩展和更改核心功能时,可以轻松地为特定队列使用自定义服务。
我公司记得在 Drupal 7 中,为了让队列按需求工作,不得不重写很多内置的队列功能。而在 Drupal 8+ 中,就不再需要这样做了,这无疑是个好迹象。不过,如果你确实需要更改某些内容,那么很容易插入自定义队列处理程序,而无需编写大量代码。
Queue Unique 模块创建了一个强制所有队列项唯一的队列,该模块使用 settings.php 文件中的 $settings 数组来强制哪些队列应被视为唯一。RabbitMQ 模块通过 Drupal 队列系统提供与 RabbitMQ 的接口,也做了类似的事情。这两个模块都可作为 Drupal 模块开发的优秀示例。
本文所涉及的所有代码都能在我公司的 GitHub 上的 Drupal 队列示例仓库中找到,特别是 queue_custom_example 模块。队列自定义示例模块展示了如何使用直接队列服务和更新 $settings 队列名称来管理队列中的项。此外,在进行 Drupal 升级到 Drupal11 的过程中,如果涉及到队列管理的调整,这些示例代码也能提供很好的参考。


