RabbitMqBundle

repository·master·Indexed 22 days ago

https://github.com/php-amqplib/rabbitmqbundle

A Symfony bundle that integrates RabbitMQ messaging into applications using the php-amqplib library. It provides abstractions for producers and consumers, allowing developers to publish and process messages via services and CLI commands. Features include support for RabbitMQ clusters, RPC patterns, fair dispatching via qos_options, and integration with Symfony's EventDispatcher and Dependency Injection.

Tokens
9.7K
Snippets
28
Records
48
Agent score
78%

What's inside rabbitmqbundle

  1. Use Multiple Consumers to handle multiple queues

    master

    Instead of creating one worker per queue, you can use multiple_consumers to listen to several queues within a single consumer process. This is useful for grouping related tasks and reducing the number of active workers.

    • Each queue under a multiple_consumers definition must specify its own name, callback, and routing_keys.
    • The callback for each queue must implement ConsumerInterface.
    • You can optionally provide a queues_provider service that implements QueuesProviderInterface to dynamically supply queues.
    • All queues in a multiple_consumers block share the same exchange.
    multiple_consumers:
        upload:
            connection:       default
            exchange_options: {name: 'upload', type: direct}
            queues_provider: queues_provider_service
            queues:
                upload-picture:
                    name:     upload_picture
                    callback: upload_picture_service
                    routing_keys:
                        - picture
                upload-video:
                    name:     upload_video
                    callback: upload_video_service
                    routing_keys:
                        - video
                upload-stats:
                    name:     upload_stats
                    callback: upload_stats
  2. Autowire Producers and Consumers in Symfony 4.2+

    master

    In Symfony 4.2+, the bundle automatically creates container aliases for producers and consumers to support dependency injection via type-hinting.

    • Producers: All producers are aliased to OldSound\RabbitMqBundle\RabbitMq\ProducerInterface. It is highly recommended to use this interface for type-hinting.
    • Consumers: All consumers are aliased to OldSound\RabbitMqBundle\RabbitMq\ConsumerInterface.

    Argument Naming Convention: The argument name in your controller/service is constructed from the configuration key and suffixed with Producer or Consumer. For example, a configuration key upload_picture will be autowired if you use the argument name $uploadPictureProducer.

    public function indexAction($name, ProducerInterface $uploadPictureProducer)
    {
        $msg = array('user_id' => 1235, 'image_path' => '/path/to/new/pic.png');
        $uploadPictureProducer->publish(serialize($msg));
    }
  3. Publish data via STDIN Producer

    master

    You can use a command-line tool to pipe data from standard input (STDIN) directly into a RabbitMQ queue. This is useful for composing producers with Unix pipes.

    1. Define a producer in your configuration.
    2. Pipe your data into the rabbitmq:stdin-producer command followed by the producer name.
    producers:
        words:
          connection:       default
          exchange_options: {name: 'words', type: direct}
    $ find vendor/symfony/ -name "*.xml" -print0 | xargs -0 cat | ./app/console rabbitmq:stdin-producer words
  4. Use Producers to send messages

    master

    A producer sends messages to an exchange in the RabbitMQ broker. In your configuration, you define producers with connection options and exchange options (name and type).

    When you define a producer named upload_picture in your configuration, the bundle provides a service in the container named old_sound_rabbit_mq.upload_picture_producer.

    To send a message, use the OldSound\RabbitMqBundle\RabbitMq\Producer#publish() method. This method accepts:

    1. The message body (string).
    2. An optional routing key.
    3. An optional array of additional properties to customize the PhpAmqpLib\Message\AMQPMessage (e.g., changing headers).

    You can also explicitly set the content type or delivery mode on the producer instance using setContentType() and setDeliveryMode(). Defaults are text/plain and 2 respectively.

    public function indexAction($name)
    {
        $msg = array('user_id' => 1235, 'image_path' => '/path/to/new/pic.png');
        $this->get('old_sound_rabbit_mq.upload_picture_producer')->publish(serialize($msg));
    }
  5. Implement RPC (Remote Procedure Call) with Clients and Servers

    master

    The bundle provides built-in support for RPC patterns using Symfony services.

    1. Configure the RPC Server and Client

    Define rpc_servers to handle requests and rpc_clients to send them.

    2. Start the RPC Server

    Run the server from the CLI:

    ./app/console_dev rabbitmq:rpc-server <server_name>

    3. Use the RPC Client in a Controller

    To use a client, fetch the service named old_sound_rabbit_mq.<client_id>_rpc. Use addRequest to send a message and getReplies() to retrieve the results. getReplies() is a blocking call.

    Request Parameters:

    • args: The data to send (usually serialized).
    • server: The name of the RPC server.
    • requestId: A unique identifier for the request to find the reply in the results array.
    • routingKey (optional)
    • expiration (optional): Time in milliseconds before the request times out (requires RabbitMQ 3.x+).

    Handling Timeouts: If a request exceeds the expiration, getReplies() will throw a \PhpAmqpLib\\Exception\\AMQPTimeoutException.

  6. Install RabbitMqBundle in Symfony (>= 4.4)

    master

    To use RabbitMqBundle in a Symfony application (version 4.4 or higher), install the bundle and its dependencies via Composer and then register it in your application kernel.

    $ composer require php-amqplib/rabbitmq-bundle
    // app/AppKernel.php
    
    public function registerBundles()
    {
        $bundles = array(
            new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(),
        );
    }
  7. Implement and Run Batch Consumers

    master

    Batch consumers allow you to process a collection of messages at once (e.g., for batch database inserts).

    Implementation

    Your callback service must implement BatchConsumerInterface and its batchExecute(array $messages) method.

    Return Values for batchExecute:

    • Return true to acknowledge (ack) all messages in the batch.
    • Return an array where keys are delivery tags and values indicate the action:
      • true: ack
      • false: reject and drop
      • -1: reject and requeue
      • nack and requeue: (specific logic for nack)

    Configuration Example

    batch_consumers:
        batch_basic_consumer:
            connection:       default
            exchange_options: {name: 'batch', type: fanout}
            queue_options:    {name: 'batch'}
            callback:         batch.basic
            qos_options:      {prefetch_size: 0, prefetch_count: 2, global: false}
            timeout_wait:     5
            auto_setup_fabric: false
            idle_timeout_exit_code: -2
            keep_alive: false
            graceful_max_execution:
                timeout: 60

    Running the Consumer

    Use the rabbitmq:batch:consumer command.

    • Use -w to run.
    • Use -b|batches <number> to stop the consumer after a specific number of batches have been consumed.
    namespace AppBundle\
    Service;
    
    use OldSound\RabbitMqBundle\RabbitMq\BatchConsumerInterface;
    use PhpAmqpLib\Message\AMQPMessage;
    
    class DevckBasicConsumer implements BatchConsumerInterface
    {
        /**
         * @inheritDoc
         */
        public function batchExecute(array $messages)
        {
            $result = [];
            /** @var AMQPMessage $message */
            foreach ($messages as $message) {
                $result[$message->getDeliveryTag()] = $this->executeSomeLogicPerMessage($message);
            }
            return $result;
        }
    }
    $ ./bin/console rabbitmq:batch:consumer batch_basic_consumer -w
  8. Use Dynamic Consumers

    master

    Dynamic consumers allow you to define queue options programmatically at runtime. This is useful when a consumer needs to handle a dynamic number of topics without changing static configuration.

    1. Implement QueueOptionsProviderInterface in a service.
    2. Add that service to the queue_options_provider key in your dynamic_consumers configuration.
    3. Run the consumer using the rabbitmq:dynamic-consumer command.
    dynamic_consumers:
        proc_logs:
            connection: default
            exchange_options: {name: 'logs', type: topic}
            callback: parse_logs_service
            queue_options_provider: queue_options_provider_service
    $ ./app/console rabbitmq:dynamic-consumer proc_logs server1
  9. Use Consumers to process messages

    master

    A consumer connects to the server and runs a loop waiting for messages. In your configuration, you define a consumer with:

    • connection: The connection to use.
    • exchange_options: Must match the producer's exchange options.
    • queue_options: Specifies the name of the queue.
    • callback: A reference to a service that will be executed when a message is received.

    The callback receives an instance of PhpAmqpLib\Message\AMQPMessage. You can access the message content via $msg->body.

    consumers:
        upload_picture:
            connection:       default
            exchange_options: {name: 'upload-picture', type: direct}
            queue_options:    {name: 'upload-picture'}
            callback:         upload_picture_service
  10. Install RabbitMqBundle in standalone Symfony Console applications

    master

    If you are building a console application using Symfony's Dependency Injection and Config components (without the full HttpKernel), you can load the bundle by registering its extension and compiler pass manually.

    {
        "require": {
            "php-amqplib/rabbitmq-bundle": "^2.0"
        }
    }
    use OldSound\RabbitMqBundle\DependencyInjection\OldSoundRabbitMqExtension;
    use OldSound\RabbitMqBundle\DependencyInjection\Compiler\RegisterPartsPass;
    
    // ...
    
    $containerBuilder->registerExtension(new OldSoundRabbitMqExtension());
    $containerBuilder->addCompilerPass(new RegisterPartsPass());
  11. Use Anonymous Consumers for Topic Monitoring

    master

    Anonymous consumers are useful for temporary monitoring (e.g., watching logs via a topic exchange). Instead of managing a named queue, the consumer automatically creates a random queue, binds it to the exchange with a specific routing key, and handles unbinding/deletion.

    1. Define an anon_consumers block in your configuration specifying the exchange_options and a callback.
    2. Run the consumer using the rabbitmq:anon-consumer command, providing the routing key with the -r flag.
    anon_consumers:
        logs_watcher:
            connection:       default
            exchange_options: {name: 'app-logs', type: topic}
            callback:         logs_watcher
    $ ./app/console_dev rabbitmq:anon-consumer -m 5 -r '#.error' logs_watcher
  12. Set up the RabbitMQ fabric

    master

    To ensure that all exchanges, queues, and bindings are correctly created in RabbitMQ, you can use the rabbitmq:setup-fabric command. This is useful for declaring your entire configuration at once and preventing message loss that occurs when messages are produced to exchanges without existing queues.

    By default, consumers and producers attempt to declare their own requirements upon startup. However, if you have changed your configuration, you should run the setup command to synchronize the RabbitMQ state with your application configuration.

    $ ./app/console rabbitmq:setup-fabric