{"id":14983922,"url":"https://github.com/php-amqplib/rabbitmqbundle","last_synced_at":"2025-05-11T05:46:40.477Z","repository":{"id":43293771,"uuid":"1389143","full_name":"php-amqplib/RabbitMqBundle","owner":"php-amqplib","description":"RabbitMQ Bundle for the Symfony web framework","archived":false,"fork":false,"pushed_at":"2025-01-08T09:29:57.000Z","size":993,"stargazers_count":1232,"open_issues_count":2,"forks_count":467,"subscribers_count":52,"default_branch":"master","last_synced_at":"2025-05-11T05:46:33.943Z","etag":null,"topics":["amqp","amqp-message","consumer","messaging","php","php-amqplib","rabbitmq","symfony-bundle"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/php-amqplib.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2011-02-20T13:06:24.000Z","updated_at":"2025-05-09T10:00:07.000Z","dependencies_parsed_at":"2023-01-30T19:45:52.068Z","dependency_job_id":"1fdb2e0a-e024-4b98-b590-d9319362c465","html_url":"https://github.com/php-amqplib/RabbitMqBundle","commit_stats":{"total_commits":573,"total_committers":204,"mean_commits":"2.8088235294117645","dds":0.8778359511343805,"last_synced_commit":"8c18f6db79b59de88e57bd805192a98e48076c41"},"previous_names":[],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-amqplib%2FRabbitMqBundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-amqplib%2FRabbitMqBundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-amqplib%2FRabbitMqBundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/php-amqplib%2FRabbitMqBundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/php-amqplib","download_url":"https://codeload.github.com/php-amqplib/RabbitMqBundle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253523718,"owners_count":21921818,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amqp","amqp-message","consumer","messaging","php","php-amqplib","rabbitmq","symfony-bundle"],"created_at":"2024-09-24T14:08:10.830Z","updated_at":"2025-05-11T05:46:40.452Z","avatar_url":"https://github.com/php-amqplib.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RabbitMqBundle #\n\n[![Latest Version](http://img.shields.io/packagist/v/php-amqplib/rabbitmq-bundle.svg?style=flat-square)](https://github.com/php-amqplib/RabbitMqBundle/releases)\n[![Test](https://github.com/php-amqplib/RabbitMqBundle/actions/workflows/test.yaml/badge.svg)](https://github.com/php-amqplib/RabbitMqBundle/actions/workflows/test.yaml)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/php-amqplib/RabbitMqBundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/php-amqplib/RabbitMqBundle/?branch=master)\n[![Code Coverage](https://scrutinizer-ci.com/g/php-amqplib/RabbitMqBundle/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/php-amqplib/RabbitMqBundle/?branch=master)\n[![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat-square)](https://github.com/phpstan/phpstan)\n[![Join the chat at https://gitter.im/php-amqplib/RabbitMqBundle](https://badges.gitter.im/php-amqplib/RabbitMqBundle.svg)](https://gitter.im/php-amqplib/RabbitMqBundle?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n\n## About ##\n\nThe `RabbitMqBundle` incorporates messaging in your application via [RabbitMQ](http://www.rabbitmq.com/) using the [php-amqplib](http://github.com/php-amqplib/php-amqplib) library.\n\nThe bundle implements several messaging patterns as seen on the [Thumper](https://github.com/php-amqplib/Thumper) library. Therefore publishing messages to RabbitMQ from a Symfony controller is as easy as:\n\n```php\n$msg = array('user_id' =\u003e 1235, 'image_path' =\u003e '/path/to/new/pic.png');\n$this-\u003eget('old_sound_rabbit_mq.upload_picture_producer')-\u003epublish(serialize($msg));\n```\n\nLater when you want to consume 50 messages out of the `upload_pictures` queue, you just run on the CLI:\n\n```bash\n$ ./app/console rabbitmq:consumer -m 50 upload_picture\n```\n\nAll the examples expect a running RabbitMQ server.\n\nThis bundle was presented at [Symfony Live Paris 2011](http://www.symfony-live.com/paris/schedule#session-av1) conference. See the slides [here](http://www.slideshare.net/old_sound/theres-a-rabbit-on-my-symfony).\n\n## Version 2 ##\nDue to the breaking changes happened caused by Symfony \u003e=4.4, a new tag was released, making the bundle compatible with Symfony \u003e=4.4.\n\n## Installation ##\n\n### For Symfony Framework \u003e= 4.4 ###\n\nRequire the bundle and its dependencies with composer:\n\n```bash\n$ composer require php-amqplib/rabbitmq-bundle\n```\n\nRegister the bundle:\n\n```php\n// app/AppKernel.php\n\npublic function registerBundles()\n{\n    $bundles = array(\n        new OldSound\\RabbitMqBundle\\OldSoundRabbitMqBundle(),\n    );\n}\n```\n\nEnjoy !\n\n### For a console application that uses Symfony Console, Dependency Injection and Config components ###\n\nIf you have a console application used to run RabbitMQ consumers, you do not need Symfony HttpKernel and FrameworkBundle.\nFrom version 1.6, you can use the Dependency Injection component to load this bundle configuration and services, and then use the consumer command.\n\nRequire the bundle in your composer.json file:\n\n```\n{\n    \"require\": {\n        \"php-amqplib/rabbitmq-bundle\": \"^2.0\",\n    }\n}\n```\n\nRegister the extension and the compiler pass:\n\n```php\nuse OldSound\\RabbitMqBundle\\DependencyInjection\\OldSoundRabbitMqExtension;\nuse OldSound\\RabbitMqBundle\\DependencyInjection\\Compiler\\RegisterPartsPass;\n\n// ...\n\n$containerBuilder-\u003eregisterExtension(new OldSoundRabbitMqExtension());\n$containerBuilder-\u003eaddCompilerPass(new RegisterPartsPass());\n```\n\n### Warning - BC Breaking Changes ###\n\n* Since 2012-06-04 Some default options for exchanges declared in the \"producers\" config section\n  have changed to match the defaults of exchanges declared in the \"consumers\" section.\n  The affected settings are:\n\n  * `durable` was changed from `false` to `true`,\n  * `auto_delete` was changed from `true` to `false`.\n\n  Your configuration must be updated if you were relying on the previous default values.\n* Since 2012-04-24 The ConsumerInterface::execute method signature has changed\n* Since 2012-01-03 the consumers execute method gets the whole AMQP message object and not just the body. See the CHANGELOG file for more details.\n\n## Usage ##\n\nAdd the `old_sound_rabbit_mq` section in your configuration file:\n\n```yaml\nold_sound_rabbit_mq:\n    connections:\n        default:\n            host:     'localhost'\n            port:     5672\n            user:     'guest'\n            password: 'guest'\n            vhost:    '/'\n            lazy:     false\n            connection_timeout: 3\n            read_write_timeout: 3\n            \n            # the timeout when waiting for a response from rabbitMQ (0.0 means waits forever)\n            channel_rpc_timeout: 0.0\n\n            # requires php-amqplib v2.4.1+ and PHP5.4+\n            keepalive: false\n\n            # requires php-amqplib v2.4.1+\n            heartbeat: 0\n\n            #requires php_sockets.dll\n            use_socket: true # default false\n            \n            login_method: 'AMQPLAIN' # default 'AMQPLAIN', can be 'EXTERNAL' or 'PLAIN', see https://www.rabbitmq.com/docs/access-control#mechanisms\n            \n        another:\n            # A different (unused) connection defined by an URL. One can omit all parts,\n            # except the scheme (amqp:). If both segment in the URL and a key value (see above)\n            # are given the value from the URL takes precedence.\n            # See https://www.rabbitmq.com/uri-spec.html on how to encode values.\n            url: 'amqp://guest:password@localhost:5672/vhost?lazy=1\u0026connection_timeout=6'\n    producers:\n        upload_picture:\n            connection:            default\n            exchange_options:      {name: 'upload-picture', type: direct}\n            service_alias:         my_app_service # no alias by default\n            default_routing_key:   'optional.routing.key' # defaults to '' if not set\n            default_content_type:  'content/type' # defaults to 'text/plain'\n            default_delivery_mode: 2 # optional. 1 means non-persistent, 2 means persistent. Defaults to \"2\".\n    consumers:\n        upload_picture:\n            connection:       default\n            exchange_options: {name: 'upload-picture', type: direct}\n            queue_options:    {name: 'upload-picture'}\n            callback:         upload_picture_service\n            options:\n                no_ack:       false # optional. If set to \"true\", automatic acknowledgement mode will be used by this consumer. Default \"false\". See https://www.rabbitmq.com/confirms.html for details.\n```\n\nHere we configure the connection service and the message endpoints that our application will have. In this example your service container will contain the service `old_sound_rabbit_mq.upload_picture_producer` and `old_sound_rabbit_mq.upload_picture_consumer`. The later expects that there's a service called `upload_picture_service`.\n\nIf you don't specify a connection for the client, the client will look for a connection with the same alias. So for our `upload_picture` the service container will look for an `upload_picture` connection.\n\nIf you need to add optional queue arguments, then your queue options can be something like this:\n\n```yaml\nqueue_options: {name: 'upload-picture', arguments: {'x-ha-policy': ['S', 'all']}}\n```\n\nanother example with message TTL of 20 seconds:\n\n```yaml\nqueue_options: {name: 'upload-picture', arguments: {'x-message-ttl': ['I', 20000]}}\n```\n\nThe argument value must be a list of datatype and value. Valid datatypes are:\n\n* `S` - String\n* `I` - Integer\n* `D` - Decimal\n* `T` - Timestamps\n* `F` - Table\n* `A` - Array\n* `t` - Bool\n\nAdapt the `arguments` according to your needs.\n\nIf you want to bind queue with specific routing keys you can declare it in producer or consumer config:\n\n```yaml\nqueue_options:\n    name: \"upload-picture\"\n    routing_keys:\n      - 'android.#.upload'\n      - 'iphone.upload'\n```\n\n### Important notice - Lazy Connections ###\n\nIn a Symfony environment all services are fully bootstrapped for each request, from version \u003e= 4.3 you can declare\na service as lazy ([Lazy Services](http://symfony.com/doc/master/components/dependency_injection/lazy_services.html)).\nThis bundle still doesn't support new Lazy Services feature but you can set `lazy: true` in your connection\nconfiguration to avoid unnecessary connections to your message broker in every request.\nIt's extremely recommended to use lazy connections because performance reasons, nevertheless lazy option is disabled\nby default to avoid possible breaks in applications already using this bundle.\n\n### Important notice - Heartbeats ###\n\nIt's a good idea to set the ```read_write_timeout``` to 2x the heartbeat so your socket will be open. If you don't do this, or use a different multiplier, there's a risk the __consumer__ socket will timeout.\n\nPlease bear in mind, that you can expect problems, if your tasks are generally running longer than the heartbeat period, to which there are no good solutions ([link](https://github.com/php-amqplib/RabbitMqBundle/issues/301)). \nConsider using either a big value for the heartbeat or leave the heartbeat disabled in favour of the tcp's `keepalive` (both on the client and server side) and the `graceful_max_execution_timeout` feature. \n\n### Multiple Hosts ###\n\nYou can provide multiple hosts for a connection. This will allow you to use RabbitMQ cluster with multiple nodes.\n\n```yaml\n  old_sound_rabbit_mq:\n      connections:\n          default:\n              hosts:\n                - host: host1\n                  port: 3672\n                  user: user1\n                  password: password1\n                  vhost: vhost1\n                - url: 'amqp://guest:password@localhost:5672/vhost'\n              connection_timeout: 3\n              read_write_timeout: 3\n```\n\nPay attention that you can not specify \n```yaml\n  connection_timeout \n  read_write_timeout\n  use_socket\n  ssl_context\n  keepalive\n  heartbeat\n  connection_parameters_provider \n```\nparameters to each host separately.\n\n### Dynamic Connection Parameters ###\n\nSometimes your connection information may need to be dynamic. Dynamic connection parameters allow you to supply or\noverride parameters programmatically through a service.\n\ne.g. In a scenario when the `vhost` parameter of the connection depends on the current tenant of your white-labeled\napplication and you do not want (or can't) change it's configuration every time.\n\nDefine a service under `connection_parameters_provider` that implements the `ConnectionParametersProviderInterface`,\nand add it to the appropriate `connections` configuration.\n\n```yaml\nconnections:\n    default:\n        host:     'localhost'\n        port:     5672\n        user:     'guest'\n        password: 'guest'\n        vhost:    'foo' # to be dynamically overridden by `connection_parameters_provider`\n        connection_parameters_provider: connection_parameters_provider_service\n```\n\nExample Implementation:\n\n```php\nclass ConnectionParametersProviderService implements ConnectionParametersProvider {\n    ...\n    public function getConnectionParameters() {\n        return array('vhost' =\u003e $this-\u003egetVhost());\n    }\n    ...\n}\n```\n\nIn this case, the `vhost` parameter will be overridden by the output of `getVhost()`.\n\n## Producers, Consumers, What? ##\n\nIn a messaging application, the process sending messages to the broker is called __producer__ while the process receiving those messages is called __consumer__. In your application you will have several of them that you can list under their respective entries in the configuration.\n\n### Producer ###\n\nA producer will be used to send messages to the server. In the AMQP Model, messages are sent to an __exchange__, this means that in the configuration for a producer you will have to specify the connection options along with the exchange options, which usually will be the name of the exchange and the type of it.\n\nNow let's say that you want to process picture uploads in the background. After you move the picture to its final location, you will publish a message to server with the following information:\n\n```php\npublic function indexAction($name)\n{\n    $msg = array('user_id' =\u003e 1235, 'image_path' =\u003e '/path/to/new/pic.png');\n    $this-\u003eget('old_sound_rabbit_mq.upload_picture_producer')-\u003epublish(serialize($msg));\n}\n```\n\nAs you can see, if in your configuration you have a producer called __upload\\_picture__, then in the service container you will have a service called __old_sound_rabbit_mq.upload\\_picture\\_producer__.\n\nBesides the message itself, the `OldSound\\RabbitMqBundle\\RabbitMq\\Producer#publish()` method also accepts an optional routing key parameter and an optional array of additional properties. The array of additional properties allows you to alter the properties with which an `PhpAmqpLib\\Message\\AMQPMessage` object gets constructed by default. This way, for example, you can change the application headers.\n\nYou can use __setContentType__ and __setDeliveryMode__ methods in order to set the message content type and the message delivery mode respectively, overriding any default set in the \"producers\" config section. If not overriden by either the \"producers\" configuration or an explicit call to these methods (as per the below example), the default values are __text/plain__ for content type and __2__ for delivery mode.\n\n```php\n$this-\u003eget('old_sound_rabbit_mq.upload_picture_producer')-\u003esetContentType('application/json');\n```\n\nIf you need to use a custom class for a producer (which should inherit from `OldSound\\RabbitMqBundle\\RabbitMq\\Producer`), you can use the `class` option:\n\n```yaml\n    ...\n    producers:\n        upload_picture:\n            class: My\\Custom\\Producer\n            connection: default\n            exchange_options: {name: 'upload-picture', type: direct}\n    ...\n```\n\n\nThe next piece of the puzzle is to have a consumer that will take the message out of the queue and process it accordingly.\n\n#### Producer Events ####\n\nThere are currently two events emitted by the producer.\n\n##### BeforeProducerPublishMessageEvent #####\nThis event occurs immediately before publishing the message. This is a good hook to do any final logging, validation, etc. before actually sending the message. A sample implementation of a listener:\n\n```php\nnamespace App\\EventListener;\n\nuse OldSound\\RabbitMqBundle\\Event\\BeforeProducerPublishMessageEvent;\nuse Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener;\n\n#[AsEventListener(event: BeforeProducerPublishMessageEvent::NAME)]\nfinal class AMQPBeforePublishEventListener\n{\n    public function __invoke(BeforeProducerPublishMessageEvent $event): void\n    {\n        // Your code goes here\n    }\n}\n```\n\n##### AfterProducerPublishMessageEvent #####\nThis event occurs immediately after publishing the message. This is a good hook to do any confirmation logging, commits, etc. after actually sending the message. A sample implementation of a listener:\n\n```php\nnamespace App\\EventListener;\n\nuse OldSound\\RabbitMqBundle\\Event\\AfterProducerPublishMessageEvent;\nuse Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener;\n\n#[AsEventListener(event: AfterProducerPublishMessageEvent::NAME)]\nfinal class AMQPBeforePublishEventListener\n{\n    public function __invoke(AfterProducerPublishMessageEvent $event): void\n    {\n        // Your code goes here\n    }\n}\n```\n\n\n### Consumers ###\n\nA consumer will connect to the server and start a __loop__  waiting for incoming messages to process. Depending on the specified __callback__ for such consumer will be the behavior it will have. Let's review the consumer configuration from above:\n\n```yaml\nconsumers:\n    upload_picture:\n        connection:       default\n        exchange_options: {name: 'upload-picture', type: direct}\n        queue_options:    {name: 'upload-picture'}\n        callback:         upload_picture_service\n```\n\nAs we see there, the __callback__ option has a reference to an __upload\\_picture\\_service__. When the consumer gets a message from the server it will execute such callback. If for testing or debugging purposes you need to specify a different callback, then you can change it there.\n\nApart from the callback we also specify the connection to use, the same way as we do with a __producer__. The remaining options are the __exchange\\_options__ and the __queue\\_options__. The __exchange\\_options__ should be the same ones as those used for the __producer__. In the __queue\\_options__ we will provide a __queue name__. Why?\n\nAs we said, messages in AMQP are published to an __exchange__. This doesn't mean the message has reached a __queue__. For this to happen, first we need to create such __queue__ and then bind it to the __exchange__. The cool thing about this is that you can bind several __queues__ to one __exchange__, in that way one message can arrive to several destinations. The advantage of this approach is the __decoupling__ from the producer and the consumer. The producer does not care about how many consumers will process his messages. All it needs is that his message arrives to the server. In this way we can expand the actions we perform every time a picture is uploaded without the need to change code in our controller.\n\nNow, how to run a consumer? There's a command for it that can be executed like this:\n\n```bash\n$ ./app/console rabbitmq:consumer -m 50 upload_picture\n```\n\nWhat does this mean? We are executing the __upload\\_picture__ consumer telling it to consume only 50 messages. Every time the consumer receives a message from the server, it will execute the configured callback passing the AMQP message as an instance of the `PhpAmqpLib\\Message\\AMQPMessage` class. The message body can be obtained by calling `$msg-\u003ebody`. By default the consumer will process messages in an __endless loop__ for some definition of _endless_.\n\nIf you want to be sure that consumer will finish executing instantly on Unix signal, you can run command with flag `-w`.\n\n```bash\n$ ./app/console rabbitmq:consumer -w upload_picture\n```\n\nThen the consumer will finish executing instantly.\n\nFor using command with this flag you need to install PHP with [PCNTL extension](http://www.php.net/manual/en/book.pcntl.php).\n\nIf you want to establish a consumer memory limit, you can do it by using flag `-l`. In the following example, this flag adds 256 MB memory limit. Consumer will be stopped five MB before reaching 256MB in order to avoid a PHP Allowed memory size error.\n\n```bash\n$ ./app/console rabbitmq:consumer -l 256\n```\n\nIf you want to remove all the messages awaiting in a queue, you can execute this command to purge this queue:\n\n```bash\n$ ./app/console rabbitmq:purge --no-confirmation upload_picture\n```\n\nFor deleting the consumer's queue, use this command:\n\n```bash\n$ ./app/console rabbitmq:delete --no-confirmation upload_picture\n```\n\n#### Consumer Events ####\n\nThis can be useful in many scenarios.\nThere are 3 AMQPEvents:\n##### ON CONSUME #####\n```php\nclass OnConsumeEvent extends AMQPEvent\n{\n    const NAME = AMQPEvent::ON_CONSUME;\n\n    /**\n     * OnConsumeEvent constructor.\n     *\n     * @param Consumer $consumer\n     */\n    public function __construct(Consumer $consumer)\n    {\n        $this-\u003esetConsumer($consumer);\n    }\n}\n```\n\nLet`s say you need to sleep / stop consumer/s on a new application deploy.\nYou can listen for `OldSound\\RabbitMqBundle\\Event\\OnConsumeEvent` and check for new application deploy.\n\n##### BEFORE PROCESSING MESSAGE #####\n\n```php\nclass BeforeProcessingMessageEvent extends AMQPEvent\n{\n    const NAME = AMQPEvent::BEFORE_PROCESSING_MESSAGE;\n\n    /**\n     * BeforeProcessingMessageEvent constructor.\n     *\n     * @param AMQPMessage $AMQPMessage\n     */\n    public function __construct(Consumer $consumer, AMQPMessage $AMQPMessage)\n    {\n        $this-\u003esetConsumer($consumer);\n        $this-\u003esetAMQPMessage($AMQPMessage);\n    }\n}\n``` \nEvent raised before processing a `AMQPMessage`.\n\n##### AFTER PROCESSING MESSAGE #####\n\n```php\nclass AfterProcessingMessageEvent extends AMQPEvent\n{\n    const NAME = AMQPEvent::AFTER_PROCESSING_MESSAGE;\n\n    /**\n     * AfterProcessingMessageEvent constructor.\n     *\n     * @param AMQPMessage $AMQPMessage\n     */\n    public function __construct(Consumer $consumer, AMQPMessage $AMQPMessage)\n    {\n        $this-\u003esetConsumer($consumer);\n        $this-\u003esetAMQPMessage($AMQPMessage);\n    }\n}\n``` \nEvent raised after processing a `AMQPMessage`.\nIf the process message will throw an Exception the event will not raise.\n\n##### IDLE MESSAGE #####\n\n```php\n\u003c?php\nclass OnIdleEvent extends AMQPEvent\n{\n    const NAME = AMQPEvent::ON_IDLE;\n\n    /**\n     * OnIdleEvent constructor.\n     *\n     * @param AMQPMessage $AMQPMessage\n     */\n    public function __construct(Consumer $consumer)\n    {\n        $this-\u003esetConsumer($consumer);\n        \n        $this-\u003eforceStop = true;\n    }\n}\n```\n\nEvent raised when `wait` method exit by timeout without receiving a message. \nIn order to make use of this event a consumer `idle_timeout` has to be [configured](#idle-timeout). \nBy default process exit on idle timeout, you can prevent it by setting `$event-\u003esetForceStop(false)` in a listener.\n\n#### Idle timeout ####\n\nIf you need to set a timeout when there are no messages from your queue during a period of time, you can set the `idle_timeout` in seconds.\nThe `idle_timeout_exit_code` specifies what exit code should be returned by the consumer when the idle timeout occurs. Without specifying it, the consumer will throw an `PhpAmqpLib\\Exception\\AMQPTimeoutException` exception.\n\n```yaml\nconsumers:\n    upload_picture:\n        connection:             default\n        exchange_options:       {name: 'upload-picture', type: direct}\n        queue_options:          {name: 'upload-picture'}\n        callback:               upload_picture_service\n        idle_timeout:           60\n        idle_timeout_exit_code: 0\n```\n\n#### Timeout wait ####\n\nSet the `timeout_wait` in seconds.\nThe `timeout_wait` specifies how long the consumer will wait without receiving a new message before ensuring the current connection is still valid.\n\n```yaml\nconsumers:\n    upload_picture:\n        connection:             default\n        exchange_options:       {name: 'upload-picture', type: direct}\n        queue_options:          {name: 'upload-picture'}\n        callback:               upload_picture_service\n        idle_timeout:           60\n        idle_timeout_exit_code: 0\n        timeout_wait:           10\n```\n\n#### Graceful max execution timeout ####\n\nIf you'd like your consumer to be running up to certain time and then gracefully exit, then set the `graceful_max_execution.timeout` in seconds.\n\"Gracefully exit\" means, that the consumer will exit either after the currently running task or immediatelly, when waiting for new tasks.\nThe `graceful_max_execution.exit_code` specifies what exit code should be returned by the consumer when the graceful max execution timeout occurs. Without specifying it, the consumer will exit with status `0`.\n\nThis feature is great in conjuction with supervisord, which together can allow for periodical memory leaks cleanup, connection with database/rabbitmq renewal and more.\n\n```yaml\nconsumers:\n    upload_picture:\n        connection:             default\n        exchange_options:       {name: 'upload-picture', type: direct}\n        queue_options:          {name: 'upload-picture'}\n        callback:               upload_picture_service\n\n        graceful_max_execution:\n            timeout: 1800 # 30 minutes \n            exit_code: 10 # default is 0 \n```\n\n#### Fair dispatching ####\n\n\u003e You might have noticed that the dispatching still doesn't work exactly as we want. For example in a situation with two workers, when all odd messages are heavy and even messages are light, one worker will be constantly busy and the other one will do hardly any work. Well, RabbitMQ doesn't know anything about that and will still dispatch messages evenly.\n\n\u003e This happens because RabbitMQ just dispatches a message when the message enters the queue. It doesn't look at the number of unacknowledged messages for a consumer. It just blindly dispatches every n-th message to the n-th consumer.\n\n\u003e In order to defeat that we can use the basic.qos method with the prefetch_count=1 setting. This tells RabbitMQ not to give more than one message to a worker at a time. Or, in other words, don't dispatch a new message to a worker until it has processed and acknowledged the previous one. Instead, it will dispatch it to the next worker that is not still busy.\n\nFrom: http://www.rabbitmq.com/tutorials/tutorial-two-python.html\n\nBe careful as implementing the fair dispatching introduce a latency that will hurt performance (see [this blogpost](http://www.rabbitmq.com/blog/2012/05/11/some-queuing-theory-throughput-latency-and-bandwidth/)). But implemeting it allow you to scale horizontally dynamically as the queue is increasing.\nYou should evaluate, as the blogpost recommends, the right value of prefetch_size accordingly with the time taken to process each message and your network performance.\n\nWith RabbitMqBundle, you can configure that qos_options per consumer like that:\n\n```yaml\nconsumers:\n    upload_picture:\n        connection:       default\n        exchange_options: {name: 'upload-picture', type: direct}\n        queue_options:    {name: 'upload-picture'}\n        callback:         upload_picture_service\n        qos_options:      {prefetch_size: 0, prefetch_count: 1, global: false}\n```\n\n### Autowiring producers and consumers ###\n\nIf used with **Symfony 4.2+** bundle declares in container set of aliases for producers and regular consumers. Those are \nused for arguments autowiring based on declared type and argument name. This allows you to change previous producer \nexample to:\n\n```php\npublic function indexAction($name, ProducerInterface $uploadPictureProducer)\n{\n    $msg = array('user_id' =\u003e 1235, 'image_path' =\u003e '/path/to/new/pic.png');\n    $uploadPictureProducer-\u003epublish(serialize($msg));\n}\n```\n\nName of argument is constructed from producer or consumer name from configuration and suffixed with producer or consumer\nword according to type. In contrast to container items naming convention word suffix (producer or consumer) will not be\nduplicated if name is already suffixed. `upload_picture` producer key will be changed to `$uploadPictureProducer`\nargument name. `upload_picture_producer` producer key would also be aliased to `$uploadPictureProducer` argument name.\nIt is best to avoid names similar in such manner.\n\nAll producers are aliased to `OldSound\\RabbitMqBundle\\RabbitMq\\ProducerInterface` and producer class option from \nconfiguration. In sandbox mode only `ProducerInterface` aliases are made. It is highly recommended to use `ProducerInterface`\nclass when type hinting arguments for producer injection.\n\nAll consumers are aliased to `OldSound\\RabbitMqBundle\\RabbitMq\\ConsumerInterface` and `%old_sound_rabbit_mq.consumer.class%`\nconfiguration option value. There is no difference between regular and sandbox mode. It is highly recommended to use\n`ConsumerInterface` when type hinting arguments for client injection.\n\n\n### Callbacks ###\n\nHere's an example callback:\n\n```php\n\u003c?php\n\n//src/Acme/DemoBundle/Consumer/UploadPictureConsumer.php\n\nnamespace Acme\\DemoBundle\\Consumer;\n\nuse OldSound\\RabbitMqBundle\\RabbitMq\\ConsumerInterface;\nuse PhpAmqpLib\\Message\\AMQPMessage;\n\nclass UploadPictureConsumer implements ConsumerInterface\n{\n    public function execute(AMQPMessage $msg)\n    {\n        //Process picture upload.\n        //$msg will be an instance of `PhpAmqpLib\\Message\\AMQPMessage` with the $msg-\u003ebody being the data sent over RabbitMQ.\n\n        $isUploadSuccess = someUploadPictureMethod();\n        if (!$isUploadSuccess) {\n            // If your image upload failed due to a temporary error you can return false\n            // from your callback so the message will be rejected by the consumer and\n            // requeued by RabbitMQ.\n            // Any other value not equal to false will acknowledge the message and remove it\n            // from the queue\n            return false;\n        }\n    }\n}\n```\n\nAs you can see, this is as simple as implementing one method: __ConsumerInterface::execute__.\n\nKeep in mind that your callbacks _need to be registered_ as normal Symfony services. There you can inject the service container, the database service, the Symfony logger, and so on.\n\nSee [https://github.com/php-amqplib/php-amqplib/blob/master/doc/AMQPMessage.md](https://github.com/php-amqplib/php-amqplib/blob/master/doc/AMQPMessage.md) for more details of what's part of a message instance.\n\nTo stop the consumer, callback can throw ```StopConsumerException``` (the last consumed message _will not_ be ack) or ```AckStopConsumerException``` (the message _will_ be ack). If using demonized, ex: supervisor, the consumer will actually restart.\n\n### Recap ###\n\nThis seems to be quite a lot of work for just sending messages, let's recap to have a better overview. This is what we need to produce/consume messages:\n\n- Add an entry for the consumer/producer in the configuration.\n- Implement your callback.\n- Start the consumer from the CLI.\n- Add the code to publish messages inside the controller.\n\nAnd that's it!\n\n### Audit / Logging ###\n\nThis was a requirement to have a traceability of messages received/published.\nIn order to enable this you'll need to add `enable_logger` config to consumers or publishers.\n\n```yaml\nconsumers:\n    upload_picture:\n        connection:       default\n        exchange_options: {name: 'upload-picture', type: direct}\n        queue_options:    {name: 'upload-picture'}\n        callback:         upload_picture_service\n        enable_logger: true\n```\n\nIf you would like you can also treat logging from queues with different handlers in monolog, by referencing channel `phpamqplib`.\n\n### RPC or Reply/Response ###\n\nSo far we just have sent messages to consumers, but what if we want to get a reply from them? To achieve this we have to implement RPC calls into our application. This bundle makes it pretty easy to achieve such things with Symfony.\n\nLet's add a RPC client and server into the configuration:\n\n```yaml\nrpc_clients:\n    integer_store:\n        connection: default #default: default\n        unserializer: json_decode #default: unserialize\n        lazy: true #default: false\n        direct_reply_to: false\nrpc_servers:\n    random_int:\n        connection: default\n        callback:   random_int_server\n        qos_options: {prefetch_size: 0, prefetch_count: 1, global: false}\n        exchange_options: {name: random_int, type: topic}\n        queue_options: {name: random_int_queue, durable: false, auto_delete: true}\n        serializer: json_encode\n```\n\n*For a full configuration reference please use the `php app/console config:dump-reference old_sound_rabbit_mq` command.*\n\nHere we have a very useful server: it returns random integers to its clients. The callback used to process the request will be the __random\\_int\\_server__ service. Now let's see how to invoke it from our controllers.\n\nFirst we have to start the server from the command line:\n\n```bash\n$ ./app/console_dev rabbitmq:rpc-server random_int\n```\n\nAnd then add the following code to our controller:\n\n```php\npublic function indexAction($name)\n{\n    $client = $this-\u003eget('old_sound_rabbit_mq.integer_store_rpc');\n    $client-\u003eaddRequest(serialize(array('min' =\u003e 0, 'max' =\u003e 10)), 'random_int', 'request_id');\n    $replies = $client-\u003egetReplies();\n}\n```\n\nAs you can see there, if our client id is __integer\\_store__, then the service name will be __old_sound_rabbit_mq.integer\\_store_rpc__. Once we get that object we place a request on the server by calling `addRequest` that expects three parameters:\n\n- The arguments to be sent to the remote procedure call.\n- The name of the RPC server, in our case __random\\_int__.\n- A request identifier for our call, in this case __request\\_id__.\n\nThe arguments we are sending are the __min__ and __max__ values for the `rand()` function. We send them by serializing an array. If our server expects JSON information, or XML, we will send such data here.\n\nThe final piece is to get the reply. Our PHP script will block till the server returns a value. The __$replies__ variable will be an associative array where each reply from the server will contained in the respective __request\\_id__ key.\n\nBy default the RPC Client expects the response to be serialized. If the server you are working with returns a non-serialized result then set the RPC client `expect_serialized_response` option to false. For example, if the **integer_store** server didn't serialize the result the client would be set as below:\n\n```yaml\nrpc_clients:\n    integer_store:\n        connection: default\n        expect_serialized_response: false\n```\n\nYou can also set a expiration for request in milliseconds, after which message will no longer be handled by server and client request will simply time out. Setting expiration for messages works only for RabbitMQ 3.x and above. Visit http://www.rabbitmq.com/ttl.html#per-message-ttl for more information.\n\n```php\npublic function indexAction($name)\n{\n    $expiration = 5000; // milliseconds\n    $client = $this-\u003eget('old_sound_rabbit_mq.integer_store_rpc');\n    $client-\u003eaddRequest($body, $server, $requestId, $routingKey, $expiration);\n    try {\n        $replies = $client-\u003egetReplies();\n        // process $replies['request_id'];\n    } catch (\\PhpAmqpLib\\Exception\\AMQPTimeoutException $e) {\n        // handle timeout\n    }\n}\n```\n\nAs you can guess, we can also make __parallel RPC calls__.\n\n### Parallel RPC ###\n\nLet's say that for rendering some webpage, you need to perform two database queries, one taking 5 seconds to complete and the other one taking 2 seconds –very expensive queries–. If you execute them sequentially, then your page will be ready to deliver in about 7 seconds. If you run them in parallel then you will have your page served in about 5 seconds. With `RabbitMqBundle` we can do such parallel calls with ease. Let's define a parallel client in the config and another RPC server:\n\n```yaml\nrpc_clients:\n    parallel:\n        connection: default\nrpc_servers:\n    char_count:\n        connection: default\n        callback:   char_count_server\n    random_int:\n        connection: default\n        callback:   random_int_server\n```\n\nThen this code should go in our controller:\n\n```php\npublic function indexAction($name)\n{\n    $client = $this-\u003eget('old_sound_rabbit_mq.parallel_rpc');\n    $client-\u003eaddRequest($name, 'char_count', 'char_count');\n    $client-\u003eaddRequest(serialize(array('min' =\u003e 0, 'max' =\u003e 10)), 'random_int', 'random_int');\n    $replies = $client-\u003egetReplies();\n}\n```\n\nIs very similar to the previous example, we just have an extra `addRequest` call. Also we provide meaningful request identifiers so later will be easier for us to find the reply we want in the __$replies__ array.\n\n### Direct Reply-To clients ###\n\nTo enable [direct reply-to clients](https://www.rabbitmq.com/direct-reply-to.html) you just have to enable option __direct_reply_to__ on the __rpc_clients__ configuration for the client.\n\nThis option will use pseudo-queue __amq.rabbitmq.reply-to__ when doing RPC calls. On the RPC server there is no modification needed.\n\n### Priority queue ###\n\nRabbitMQ has priority queue implementation in the core as of version 3.5.0. Any queue can be turned into a priority one using client-provided optional arguments (but, unlike other features that use optional arguments, not policies).\nThe implementation supports a limited number of priorities: 255. Values between 1 and 10 are recommended. [Check documentation](https://www.rabbitmq.com/priority.html)\n\nhere is how you can declare a priority queue \n\n```yml\n    consumers:\n        upload_picture:\n            connection:       default\n            exchange_options: {name: 'upload-picture', type: direct}\n            queue_options:    {name: 'upload-picture', arguments: {'x-max-priority': ['I', 10]} }\n            callback:         upload_picture_service\n\n```\n\nif `upload-picture` queue exist before you must delete this queue before you run `rabbitmq:setup-fabric` command\n\n\nNow let's say you want to make a message with high priority, you have to publish the message with this additional information \n\n```php\npublic function indexAction()\n{    \n    $msg = array('user_id' =\u003e 1235, 'image_path' =\u003e '/path/to/new/pic.png');\n    $additionalProperties = ['priority' =\u003e 10] ; \n    $routing_key = '';\n    $this-\u003eget('old_sound_rabbit_mq.upload_picture_producer')-\u003epublish(serialize($msg), $routing_key , $additionalProperties );\n}\n```\n\n### Multiple Consumers ###\n\nIt's a good practice to have a lot of queues for logic separation. With a simple consumer you will have to create one worker (consumer) per queue and it can be hard to manage when dealing\nwith many evolutions (forget to add a line in your supervisord configuration?). This is also useful for small queues as you may not want to have as many workers as queues, and want to regroup\nsome tasks together without losing flexibility and separation principle.\n\nMultiple consumers allow you to handle this use case by listening to multiple queues on the same consumer.\n\nHere is how you can set a consumer with multiple queues:\n\n```yaml\nmultiple_consumers:\n    upload:\n        connection:       default\n        exchange_options: {name: 'upload', type: direct}\n        queues_provider: queues_provider_service\n        queues:\n            upload-picture:\n                name:     upload_picture\n                callback: upload_picture_service\n                routing_keys:\n                    - picture\n            upload-video:\n                name:     upload_video\n                callback: upload_video_service\n                routing_keys:\n                    - video\n            upload-stats:\n                name:     upload_stats\n                callback: upload_stats\n```\n\nThe callback is now specified under each queues and must implement the `ConsumerInterface` like a simple consumer.\nAll the options of `queues-options` in the consumer are available for each queue.\n\nBe aware that all queues are under the same exchange, it's up to you to set the correct routing for callbacks.\n\nThe `queues_provider` is a optional service that dynamically provides queues.\nIt must implement `QueuesProviderInterface`.\n\nBe aware that queues providers are responsible for the proper calls to `setDequeuer` and that callbacks are callables\n(not `ConsumerInterface`). In case service providing queues implements `DequeuerAwareInterface`, a call to\n`setDequeuer` is added to the definition of the service with a `DequeuerInterface` currently being a `MultipleConsumer`.\n\n### Arbitrary Bindings ###\n\nYou may find that your application has a complex workflow and you need to have arbitrary binding. Arbitrary\nbinding scenarios might include exchange to exchange bindings via `destination_is_exchange` property.\n\n```yaml\nbindings:\n    - {exchange: foo, destination: bar, routing_key: 'baz.*' }\n    - {exchange: foo1, destination: foo, routing_key: 'baz.*', destination_is_exchange: true}\n```\n\nThe rabbitmq:setup-fabric command will declare exchanges and queues as defined in your producer, consumer\nand multi consumer configurations before it creates your arbitrary bindings. However, the rabbitmq:setup-fabric will\n*NOT* declare addition queues and exchanges defined in the bindings. It's up to you to make sure exchanges/queues are declared.\n\n### Dynamic Consumers ###\n\nSometimes you have to change the consumer's configuration on the fly.\nDynamic consumers allow you to define the consumers queue options programmatically, based on the context.\n\ne.g. In a scenario when the defined consumer must be responsible for a dynamic number of topics and you do not want (or can't) change it's configuration every time.\n\nDefine a service `queue_options_provider` that implements the `QueueOptionsProviderInterface`, and add it to your `dynamic_consumers` configuration.\n\n```yaml\ndynamic_consumers:\n    proc_logs:\n        connection: default\n        exchange_options: {name: 'logs', type: topic}\n        callback: parse_logs_service\n        queue_options_provider: queue_options_provider_service\n```\n\nExample Usage:\n\n```bash\n$ ./app/console rabbitmq:dynamic-consumer proc_logs server1\n```\n\nIn this case the `proc_logs` consumer runs for `server1` and it can decide over the queue options it uses.\n\n### Anonymous Consumers ###\n\nNow, why will we ever need anonymous consumers? This sounds like some internet threat or something… Keep reading.\n\nIn AMQP there's a type of exchange called __topic__ where the messages are routed to queues based on –you guess– the topic of the message. We can send logs about our application to a RabbiMQ topic exchange using as topic the hostname where the log was created and the severity of such log. The message body will be the log content and our routing keys the will be like this:\n\n- server1.error\n- server2.info\n- server1.warning\n- ...\n\nSince we don't want to be filling up queues with unlimited logs what we can do is that when we want to monitor the system, we can launch a consumer that creates a queue and attaches to the __logs__ exchange based on some topic, for example, we would like to see all the errors reported by our servers. The routing key will be something like: __\\#.error__. In such case we have to come up with a queue name, bind it to the exchange, get the logs, unbind it and delete the queue. Happily AMPQ provides a way to do this automatically if you provide the right options when you declare and bind the queue. The problem is that you don't want to remember all those options. For such reason we implemented the __Anonymous Consumer__ pattern.\n\nWhen we start an Anonymous Consumer, it will take care of such details and we just have to think about implementing the callback for when the messages arrive. Is it called Anonymous because it won't specify a queue name, but it will wait for RabbitMQ to assign a random one to it.\n\nNow, how to configure and run such consumer?\n\n```yaml\nanon_consumers:\n    logs_watcher:\n        connection:       default\n        exchange_options: {name: 'app-logs', type: topic}\n        callback:         logs_watcher\n```\n\nThere we specify the exchange name and it's type along with the callback that should be executed when a message arrives.\n\nThis Anonymous Consumer is now able to listen to Producers, which are linked to the same exchange and of type _topic_:\n\n```yaml\n    producers:\n        app_logs:\n            connection:       default\n            exchange_options: {name: 'app-logs', type: topic}\n```\n\nTo start an _Anonymous Consumer_ we use the following command:\n\n```bash\n$ ./app/console_dev rabbitmq:anon-consumer -m 5 -r '#.error' logs_watcher\n```\n\nThe only new option compared to the commands that we have seen before is the one that specifies the __routing key__: `-r '#.error'`.\n\n### Batch Consumers ###\n\nIn some cases you will want to get a batch of messages and then do some processing on all of them. Batch consumers will allow you to define logic for this type of processing.\n\ne.g: Imagine that you have a queue where you receive a message for inserting some information in the database, and you realize that if you do a batch insert is much better then by inserting one by one.\n\nDefine a callback service that implements `BatchConsumerInterface` and add the definition of the consumer to your configuration.\n\n```yaml\nbatch_consumers:\n    batch_basic_consumer:\n        connection:       default\n        exchange_options: {name: 'batch', type: fanout}\n        queue_options:    {name: 'batch'}\n        callback:         batch.basic\n        qos_options:      {prefetch_size: 0, prefetch_count: 2, global: false}\n        timeout_wait:     5\n        auto_setup_fabric: false\n        idle_timeout_exit_code: -2\n        keep_alive: false\n        graceful_max_execution:\n            timeout: 60\n```\n\n*Note*: If the `keep_alive` option is set to `true`, `idle_timeout_exit_code` will be ignored and the consumer process continues.\n\nYou can implement a batch consumer that will acknowledge all messages in one return or you can have control on what message to acknowledge.\n\n```php\nnamespace AppBundle\\Service;\n\nuse OldSound\\RabbitMqBundle\\RabbitMq\\BatchConsumerInterface;\nuse PhpAmqpLib\\Message\\AMQPMessage;\n\nclass DevckBasicConsumer implements BatchConsumerInterface\n{\n    /**\n     * @inheritDoc\n     */\n    public function batchExecute(array $messages)\n    {\n        echo sprintf('Doing batch execution%s', PHP_EOL);\n        foreach ($messages as $message) {\n            $this-\u003eexecuteSomeLogicPerMessage($message);\n        }\n\n        // you ack all messages got in batch\n        return true; \n    }\n}\n```\n\n```php\nnamespace AppBundle\\Service;\n\nuse OldSound\\RabbitMqBundle\\RabbitMq\\BatchConsumerInterface;\nuse PhpAmqpLib\\Message\\AMQPMessage;\n\nclass DevckBasicConsumer implements BatchConsumerInterface\n{\n    /**\n     * @inheritDoc\n     */\n    public function batchExecute(array $messages)\n    {\n        echo sprintf('Doing batch execution%s', PHP_EOL);\n        $result = [];\n        /** @var AMQPMessage $message */\n        foreach ($messages as $message) {\n            $result[$message-\u003egetDeliveryTag()] = $this-\u003eexecuteSomeLogicPerMessage($message);\n        }\n\n        // you ack only some messages that have return true\n        // e.g:\n        // $return = [\n        //      1 =\u003e true,\n        //      2 =\u003e true,\n        //      3 =\u003e false,\n        //      4 =\u003e true,\n        //      5 =\u003e -1,\n        //      6 =\u003e 2,\n        //  ];\n        // The following will happen:\n        //  * ack: 1,2,4\n        //  * reject and requeq: 3\n        //  * nack and requeue: 6\n        //  * reject and drop: 5\n        return $result;\n    }\n}\n```\n\nHow to run the following batch consumer:\n\n```bash\n    $ ./bin/console rabbitmq:batch:consumer batch_basic_consumer -w\n```\n\nImportant: BatchConsumers will not have the `-m|messages` option available\nImportant: BatchConsumers can also have the `-b|batches` option available if you want to only consume a specific number of batches and then stop the consumer. Give the number of the batches only if you want the consumer to stop after those batch messages were consumed!\n\n### STDIN Producer ###\n\nThere's a Command that reads data from STDIN and publishes it to a RabbitMQ queue. To use it first you have to configure a `producer` service in your configuration file like this:\n\n```yaml\nproducers:\n    words:\n      connection:       default\n      exchange_options: {name: 'words', type: direct}\n```\n\nThat producer will publish messages to the `words` direct exchange. Of course you can adapt the configuration to whatever you like.\n\nThen let's say you want to publish the contents of some XML files so they are processed by a farm of consumers. You could publish them by just using a command like this:\n\n```bash\n$ find vendor/symfony/ -name \"*.xml\" -print0 | xargs -0 cat | ./app/console rabbitmq:stdin-producer words\n```\n\nThis means you can compose producers with plain Unix commands.\n\nLet's decompose that one liner:\n\n```bash\n$ find vendor/symfony/ -name \"*.xml\" -print0\n```\n\nThat command will find all the `.xml` files inside the symfony folder and will print the file name. Each of those file names is then _piped_ to `cat` via `xargs`:\n\n```bash\n$ xargs -0 cat\n```\n\nAnd finally the output of `cat` goes directly to our producer that is invoked like this:\n\n```bash\n$ ./app/console rabbitmq:stdin-producer words\n```\n\nIt takes only one argument which is the name of the producer as you configured it in your `config.yml` file.\n\n## Other Commands ##\n\n### Setting up the RabbitMQ fabric ###\n\nThe purpose of this bundle is to let your application produce messages and publish them to some exchanges you configured.\n\nIn some cases and even if your configuration is right, the messages you are producing will not be routed to any queue because none exist. The consumer responsible for the queue consumption has to be run for the queue to be created.\n\nLaunching a command for each consumer can be a nightmare when the number of consumers is high.\n\nIn order to create exchanges, queues and bindings at once and be sure you will not lose any message, you can run the following command:\n\n```bash\n$ ./app/console rabbitmq:setup-fabric\n```\n\nWhen desired, you can configure your consumers and producers to assume the RabbitMQ fabric is already defined. To do this, add the following to your configuration:\n\n```yaml\nproducers:\n    upload_picture:\n      auto_setup_fabric: false\nconsumers:\n    upload_picture:\n      auto_setup_fabric: false\n```\n\nBy default a consumer or producer will declare everything it needs with RabbitMQ when it starts.\nBe careful using this, when exchanges or queues are not defined, there will be errors. When you've changed any configuration you need to run the above setup-fabric command to declare your configuration.\n\n\n## How To Contribute ##\n\nTo contribute just open a Pull Request with your new code taking into account that if you add new features or modify existing ones you have to document in this README what they do. If you break BC then you have to document it as well. Also you have to update the CHANGELOG. So:\n\n- Document New Features.\n- Update CHANGELOG.\n- Document BC breaking changes.\n\n## License ##\n\nSee: resources/meta/LICENSE.md\n\n## Credits ##\n\nThe bundle structure and the documentation is partially based on the [RedisBundle](http://github.com/Seldaek/RedisBundle)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphp-amqplib%2Frabbitmqbundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphp-amqplib%2Frabbitmqbundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphp-amqplib%2Frabbitmqbundle/lists"}