Unobvious RabbitMQ in Yii2 or why RabbitMQ writes to all queues at once
I want to share the practical problem of configuring the RabbitMQ queue broker in Yii2. I warn the reader that I do not have an expert opinion on working with this queue broker, but I really want to fill in the gaps of the Yii2 documentation and consolidate the result of my own torment. So, if you have ever encountered the problem that sent messages fall into all the queues that are on the queue server, you agree that this is a problem and you do not understand why this is happening, then I ask for cat.
Why can you face such behavior? For example, if you, like I did not work with RabbitMQ before, but worked for example with Gearman. Sam Gearman, simple as a railway (
return [ 'bootstrap' => [ 'queue', // The component registers its own console commands ], 'components' => [ 'queue' => [ 'class' => \yii\queue\amqp_interop\Queue::class, 'port' => 5672, 'user' => 'guest', 'password' => 'guest', 'queueName' => 'queue', 'driver' => yii\queue\amqp_interop\Queue::ENQUEUE_AMQP_LIB, // or 'dsn' => 'amqp://guest:guest@localhost:5672/%2F', // or, same as above 'dsn' => 'amqp:', ], ], ];
It would seem that everything to the point of insanity is simple, here's a familiar string with queueName , copy-paste, fix, run - it works! Making queues for other components of the system. We parallelize what can our php. Commit, Push satisfied put the task in QA and go read Habr (
Here, on the most interesting, QA interrupts us in the chat, and says that something strange is happening. For some reason, the data that workers (kosumery) write are duplicated. I'm sorry, what? It can not be. We go to check the logs. We see that the message is written to the queue, and the queue is correctly selected, hash jobId is received. No, no, we don't have any mistakes. We write satisfied QA, check again, it can not be this, we are fine.
Literally half an hour later we were distracted again, the error repeated, and a notification immediately fell on the soap - the task was transferred back to work. Well, I'm a programmer, now let's figure it out. RabbitMQ, unlike Gearman, has a web interface, in which there is a lot of information about server operation. This miracle looks like this: We
throw a couple of messages into the queue, we see in the webmord that our messages are received and processed by the worker. A casual glance observes that when we throw a message into a queue, the message rates graph jumps up in all queues.
We check the configuration a hundred more times, reread the meager documentation of Yii. We did everything right. Go read the documentation on the site rabbit'a. After a couple of tens of minutes wandering in the dark, we come across a tutorial. Immediately after the first paragraph we are introduced to the cause of our misunderstandings - Exchanges. I will not repeat the documentation, very briefly.
In RabbitMQ, we do not write a message to a queue, we write it in exchange, a sort of proxy that accepts our messages at one end and communicates with queues on the server at the other end. It is in his power to make decisions in which turn to put our data. It is interesting that in the Yii documentation there is not a single line about this. At first glance, it is not clear how to configure the exchange, dive into the intestines and in the file vendor / yiisoft / yii2-queue / src / drivers / amqp_interop / Queue.php: 176 we find the cherished property that can be set. Here I must say that there are a lot of drivers for RabbitMQ, in my case enqueue / amqp-lib is used . We expose exchangeName, we test, nothing changes. We are like a real Russian engineer, first we try, and then we go thoughtfully to read the documentation again. We read again carefully, then we go to the rabbit's web and see the following:
Several of our queues are connected to the same exchange. Bingo! Here it is the reason, one “but”, I did not connect them. Go back to the driver's gut, find the setupBroker method line 392
$this->context->bind(new AmqpBind($queue, $topic));
This is unmanaged binding.
And so, after a brief reflection, we come to the conclusion that for each queue a separate exchange must be declared, then the connection will be correct and one exchange will have only one connected queue. this way we achieve the same gearman behavior. By the way, the documentation describes in detail what the exchange is for, and as I understand it, one of the reasons is the possibility to associate several queues with exchange. But I didn’t think what kind of case it is, when it may be necessary, guys write in the comments. And write, have you encountered the situation described above, or am I doing everything wrong?