gendoria / command-queue-bundle
命令队列的Symfony扩展包
Requires
- php: >=5.5.0
- gendoria/command-queue: ~0.2.0
- psr/log: ~1.0
- symfony/dependency-injection: ~2.7|~3.0@stable
- symfony/framework-bundle: ~2.7|~3.0
- symfony/monolog-bundle: ~2.4
- symfony/yaml: ~2.7|~3.0
Requires (Dev)
- doctrine/orm: ~2.2,>=2.2.3
- jms/serializer: ~1.3
- jms/serializer-bundle: ~1.1
- symfony/console: ~2.7|~3.0
- symfony/phpunit-bridge: ~2.7|~3.0
- symfony/property-access: ~2.7|~3.0
- symfony/property-info: ~2.7|~3.0
- symfony/serializer: ~2.7|~3.0
Suggests
- jms/serializer-bundle: Allows serialization using JMS Serializer library.
- symfony/console: Required, if you want to use worker runner interface.
- symfony/serializer: Allows serialization using Symfony serializer component.
This package is auto-updated.
Last update: 2024-08-27 04:37:02 UTC
README
此扩展包实现命令队列机制,使得可以从主Symfony进程发送命令,并使用后端工作进程池执行它们,使用gendoria/command-queue
库。
此扩展包与Isobar Poland合作创建。
安装
步骤1:下载扩展包
打开命令行控制台,进入您的项目目录,并执行以下命令以下载此扩展包的最新稳定版本
$ composer require gendoria/command-queue-bundle "~0.2.0"
此命令要求您全局安装Composer,如Composer文档中的安装章节中所述。
步骤2:启用扩展包
然后,通过将其添加到项目中app/AppKernel.php
文件中注册的扩展包列表来启用扩展包
<?php // app/AppKernel.php // ... class AppKernel extends Kernel { public function registerBundles() { $bundles = array( // ... new Gendoria\CommandQueueBundle\GendoriaCommandQueueBundle(), ); // ... } // ... }
步骤3:添加扩展包配置
为了使用此扩展包,您必须在您的app/config/config.yml
中添加正确的配置。
以下是一个示例完整扩展包配置:
gendoria_command_queue: enabled: true listeners: clear_logs: true clear_entity_managers: true pools: default: send_driver: '@gendoria_command_queue_rabbit_mq_driver.driver.default' pool2: send_driver: '@gendoria_command_queue.driver.direct' routes: '\Namespaced\CommandClassOrInterface': pool2
enabled
属性可以启用或禁用扩展包(例如,在某些环境如测试中)。
listeners
部分是可选的。默认情况下,所有监听器都已开启。
池部分描述了可用的队列池。当扩展包启用时,这是必需的。必须至少有一个名为default
的池存在。
池配置只有一个参数,即send_driver
。它描述了从发送者到工作者的命令传输方法。目前,此扩展包本身不提供任何传输方法(除了直接处理驱动程序)。不过,有gendoria/command-queue-rabbitmq-bundle,它使用RabbitMQ和php-amqplib/rabbitmq-bundle
库实现传输。
下一个部分是可选的routes
。它允许定义命令路由,将特定的命令类发送到不同的池。路由是一个哈希表。键是命令类表达式,值是目标池名称。每个目标池都必须在pools
部分中定义。有关命令类表达式的更多信息,请参阅路由部分。
路由是完全可选的。如果没有定义,所有命令都将发送到默认池。此外,对于未定义路由的每个命令类,也将使用默认池。
使用
此扩展包可以使创建各种命令的分布式执行环境成为可能。开发者可以使用它,例如,将处理密集型任务委托给后端执行,而不是在前端处理(从而影响用户体验)。
命令类
命令类是一切开始的地方。它用于将处理命令所需的所有数据传递给工作进程池。
命令类必须实现Gendoria\CommandQueue\Command\CommandInterface
接口。该接口本身不包含任何方法。它用于将类“标记”为命令。
命令类可以包含任何可序列化的值。它们可以是简单类型,如整数或字符串,也可以是对象或数组。它们不应该包含资源,如数据库连接或文件句柄,因为这些不是可序列化的。
以下是一个示例命令类。此特定命令使用JMS序列化程序,但您可以使用其他序列化方法,如果需要的话。命令队列扩展包为JMS和Symfony序列化组件提供实现。
namespace Example\Namespace; use Gendoria\CommandQueue\Command\CommandInterface; use JMS\Serializer\Annotation\Type; class ParseUrlCommand implements CommandInterface { /** * Page URL. * * @var string * @Type("string") */ public $url; /** * User ID. * * @var integer * @Type("integer") */ public $userId; /** * Class constructor. * * @param string $url Page URL. * @param integer $userId User ID. */ public function __construct($url, $userId) { $this->url = $url; $this->userId = $userId; } }
如您所见,它有两个公共属性,URL和用户ID。这是一个没有自身逻辑的简单值对象。这是一个建议的方法,但您也可以在其中包含一些逻辑。
命令序列化
命令将通过某种队列发送,可能发送到完全不同的机器。由于大多数传输无法传递原始PHP对象,因此必须将命令序列化为字符串表示形式。
这是通过序列化器类完成的。在大多数情况下,您不会直接使用它们。它们被注入以发送驱动程序,并在您的命令类上执行。尽管如此,您必须为它们提供配置。
上面的命令使用了JMS序列化器。这是首选方法,但包提供了两种其他选项。
每个序列化器都实现了Gendoria\CommandQueue\Serializer\SerializerInterface
。
JMS序列化器
此序列化器作为服务gendoria_command_queue.serializer.jms
可用。它使用jms/serializer-bundle
组件创建序列化命令表示形式。所有使用说明都可以在JMSSerializerBundle文档中找到。
Symfony序列化器
此序列化器作为服务gendoria_command_queue.serializer.symfony
可用。它使用Symfony序列化组件。
此驱动程序处于开发阶段。目前没有详细的配置说明。欢迎您提交一个。
⚠️ 警告! 如果您使用
jms/serializer-bundle
并将其自己的服务注册为serializer
,则此序列化器可能不可用。JMS序列化器配置中有一个enable_short_alias
键,可以更改此行为。
空序列化器
此序列化器作为服务gendoria_command_queue.serializer.null
可用。
在某些情况下,您不需要序列化命令。为此,可以使用空序列化器。尽管如此,它主要存在于测试目的,在实际世界中很少使用。
发送命令
使用队列管理器服务发送命令。它负责根据其配置将命令路由到适当的池。
包为您提供了为每个定义的池提供的简单队列管理器和多个队列管理器,后者使用路由来决定命令将发送到哪个池。
主队列管理器可以通过服务gendoria_command_queue.manager
访问。简单队列管理器可通过服务gendoria_command_queue.manager.poolName
访问,其中poolName
是您在池配置中定义的池名称。
如果您想在控制器内部使用队列管理器,可以像下面这样调用它。我们使用与上面命令示例中相同的ParseUrlCommand。
namespace ExampleBundle\Controller; use Gendoria\CommandQueue\QueueManager\MultipleQueueManagerInterface; use Example\Namespace\ParseUrlCommand; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; /** * @Route("/") */ class ExampleController extends Controller { /** * @Route("") */ public function index() { $command = new ParseUrlCommand("http://example.com", 1); /* @var $service MultipleQueueManagerInterface */ $service = $this->get('gendoria_command_queue.manager'); $service->sendCommand($command); } }
命令处理器
命令已创建并发送。现在您必须以某种方式处理它。幸运的是,这相当简单。
首先,您必须创建一个命令处理器服务。这是一个实现``的类。
namespace ExampleBundle\CommandProcessor; use Gendoria\CommandQueue\Command\CommandInterface; use Gendoria\CommandQueue\CommandProcessor\CommandProcessorInterface; use Example\Namespace\ParseUrlCommand; class ParseUrlProcessor implements CommandProcessorInterface { /** * @param ParseUrlCommand $command */ public function process(CommandInterface $command) { //Parse your URL here } /** * {@inheritdoc} */ public function supports(CommandInterface $command) { return $command instanceof ParseUrlCommand; } }
此类必须有两个函数。首先 - supports
- 如果命令处理器可以处理给定的命令,则返回true。在大多数情况下,您只需检查命令是否是给定类的实例。
第二个函数 - process - 是命令处理实际上发生的地方。
当您有您的服务类时,您必须将其注册为服务并标记为命令处理器标记。对于上面的处理器,配置可以如下所示。
services: example_bundle.processor.parse_url: class: ExampleBundle\CommandProcessor\ParseUrlProcessor tags: - name: gendoria_command_queue.processor command: Example\Namespace\ParseUrlCommand
由于服务是懒加载的,当命令来自队列时,服务不能是抽象的或私有的。
这就结束了!
路由
默认情况下,包会将所有命令传递到默认队列(和默认工作池)。如果您想某些命令由一组特定的工作者执行,可以使用路由。一个可能的用例是,您想从特定服务器而不是默认的后端服务器发送电子邮件给用户。或者您有一些处理起来非常重的命令(如视频重编码),它们可能会阻塞默认队列。然后您可以使用路由将这些命令发送到专门的工作者池。
当使用包提供的多个队列管理器时,您可以在配置文件中定义您的路由。路由条目是一个哈希表,其中键是路由表达式,值是目标池名称。
在匹配路由时,管理器使用命令类的名称及其祖先,以及命令及其祖先实现的接口。这里有几个规则
- 类名称始终比接口名称更重要
- 子类名称比祖先类名称更重要
- 比较接口时,子类实现的接口比基类实现的接口更重要
- 如果没有检测到任何内容,则使用
default
池
因此,如果您有一个实现接口Y
的类X
,并且为X
和Y
都设置了路由,则将使用X
的路由。
如果您有一个具有继承关系的类A -> B -> C
,其中A
是最基类,而C
是子类,则将使用C
的路由。如果没有为C
设置路由,则管理器会考虑B
的路由,然后是A
的路由。如果没有检测到任何内容,则将使用default
池。
此外,您可以在路由表达式中使用*
来指示任何字符串值。例如,如果您的表达式是*Something
,则它将匹配类ASomething
和BSomething
,但不匹配SomethingC
。类似地,表达式Example\Namespace\*
将匹配example命名空间中的所有命令。
通配符路由比简单路由在单个类/接口比较中不太重要。当涉及到继承和/或接口时,它变得更加复杂和难以确定。我们建议在大多数子类上使用路由,并使用通配符,除非绝对必要,否则不要为基类/接口创建路由。
发送驱动器
包可以使用任意传输机制将命令发送到命令处理器。这些机制称为“发送驱动器”。
默认情况下,包仅提供直接处理驱动器服务。它在发送命令后立即执行,在发送者相同的进程中。它不序列化命令,因此在发送之前不需要序列化配置。
其他发送驱动器(例如,使用RabbitMQ队列机制的驱动器)由其他包提供。
队列管理器
文档正在编写中