RabbitMQ receives message but it goes nowhere - php

I have a RabbitMQ installation on a PuPHPet-generated virtual box (Ubuntu 16.04 x64) on top of Windows 10.
After the setup was complete, I configured a new user using rabbitmqctl:
# rabbitmqctl add_user root root
# rabbitmqctl set_permissions -p / root ".*" ".*" ".*"
# rabbitmqctl set_user_tags root administrator
Following the PHP & RabbitMQ Tutorial, I set up a sender and receiver script.
The sender script is the following:
<?php
require_once 'vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$connection = new AMQPStreamConnection('localhost', 5672, 'root', 'root');
$channel = $connection->channel();
$channel->queue_declare('my_queue', false, false, false, false);
$msg = new AMQPMessage('Hello World!');
$channel->basic_publish($msg, '', 'hello');
echo "Sent 'Hello World!'\n";
$channel->close();
$connection->close();
And the receiver script is the following:
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
$connection = new AMQPStreamConnection('localhost', 5672, 'root', 'root');
$channel = $connection->channel();
$channel->queue_declare('my_queue', false, false, false, false);
echo ' [*] Waiting for messages. To exit press CTRL+C', "\n";
$callback = function($msg) {
echo " [x] Received ", $msg->body, "\n";
};
$channel->basic_consume('my_queue', '', false, true, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
?>
I opened a screen for the receiver script and fired off a few executions of the sender script:
$ php sender.php
Sent 'Hello World!'
The sender script didn't run into any errors (and I was able to verify that the queue was declared in rabbit), but the receiver did not output that it had received / consumed any messages.
Furthermore, a quick check using the admin manager plugin shows that the queue has no messages at all:
$ curl -i -u root:root http://localhost:15672/api/queues
...
{
"messages_details":{
"rate":0.0
},
"messages":0,
"messages_unacknowledged_details":{
"rate":0.0
},
"messages_unacknowledged":0,
"messages_ready_details":{
"rate":0.0
},
"messages_ready":0,
"reductions_details":{
"rate":0.0
},
"reductions":9294,
"node":"rabbit#leon",
"arguments":{
},
"exclusive":false,
"auto_delete":false,
"durable":false,
"vhost":"/",
"name":"my_queue",
"message_bytes_paged_out":0,
"messages_paged_out":0,
"backing_queue_status":{
"avg_ack_egress_rate":0.0,
"avg_ack_ingress_rate":0.0,
"avg_egress_rate":0.0,
"avg_ingress_rate":0.0,
"delta":[
"delta",
"undefined",
0,
0,
"undefined"
],
"len":0,
"mode":"default",
"next_seq_id":0,
"q1":0,
"q2":0,
"q3":0,
"q4":0,
"target_ram_count":"infinity"
},
"head_message_timestamp":null,
"message_bytes_persistent":0,
"message_bytes_ram":0,
"message_bytes_unacknowledged":0,
"message_bytes_ready":0,
"message_bytes":0,
"messages_persistent":0,
"messages_unacknowledged_ram":0,
"messages_ready_ram":0,
"messages_ram":0,
"garbage_collection":{
"minor_gcs":12,
"fullsweep_after":65535,
"min_heap_size":233,
"min_bin_vheap_size":46422,
"max_heap_size":0
},
"state":"running",
"recoverable_slaves":null,
"consumers":0,
"exclusive_consumer_tag":null,
"effective_policy_definition":[
],
"operator_policy":null,
"policy":null,
"consumer_utilisation":null,
"idle_since":"2018-01-28 15:21:22",
"memory":9640
},
...
It looks like the message is accepted but is immediately discarded & not logged. Speaking of logs, there's nothing suspicious in the rabbit log either. Just a lot of this:
2018-01-28 15:47:43.654 [info] <0.1417.0> accepting AMQP connection <0.1417.0> ([::1]:49058 -> [::1]:5672)
2018-01-28 15:47:43.696 [info] <0.1417.0> connection <0.1417.0> ([::1]:49058 -> [::1]:5672): user 'root' authenticated and granted access to vhost '/'
2018-01-28 15:47:43.742 [info] <0.1417.0> closing AMQP connection <0.1417.0> ([::1]:49058 -> [::1]:5672, vhost: '/', user: 'root')
Why aren't the messages coming through?

Your code seems perfectly fine except for that you have sent the message to a wrong queue. When you send a message to a queue that doesn't exist, RabbitMQ will simply discard the message since it doesn't know where to send it.
In your code, you use the default exchange to send the message. That is:
$channel->basic_publish($msg, '', 'hello');
Here we use the default or nameless exchange: messages are routed to
the queue with the name specified by routing_key, if it exists. The
routing key is the third argument to basic_publish
https://www.rabbitmq.com/tutorials/tutorial-three-php.html
so when you use the default exchange, you have to specify the routing key as the third parameter, which is your queue name. RabbitMQ creates routing keys with the same name as queues when you use the default exchange.
To fix your code, simply change hello to your queue name, i.e my_queue and it will start sending and receiving.
Hope it helps :)

The RabbitMQ team monitors this mailing list and only sometimes answers questions on StackOverflow.
Since you did not bind my_queue to any exchange, you must publish to the default exchange using my_queue as the routing key. All queues are bound to the default topic exchange using the queue name as the routing key.
In your code, you are using hello as the routing key.

Related

RabbitMQ consumer gets doubled after sometime

We are using RabbitMQ v3.7.5, AMQP 0.9.1, Erlang 20.1.7.1, PHP 7, centos 7
Our consumers are written using php-amqplib v2.9.1
We start our consumers by calling a URL using curl. The consumers work as expected for sometime, but after sometime all consumers gets doubled i.e it shows two connection running for same consumer. Once this happens, none of the consumers respond when there is data on the queue but all the data received on the queue is lost.
Has anyone faced the same issue?
Consumer Code (I am using the same code given on the official site: https://www.rabbitmq.com/tutorials/tutorial-two-php.html)
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
echo " [*] Waiting for messages. To exit press CTRL+C\n";
$callback = function ($msg) {
echo ' [x] Received ', $msg->body, "\n";
sleep(substr_count($msg->body, '.'));
echo " [x] Done\n";
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};
$channel->basic_qos(null, 1, null);
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);
while (count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
Expected Result: The consumer should not be duplicated.

Swoole with RabbitMQ

I'm trying to send some data from php application to the user's browser using websockets. Therefore I've decided to use Swoole in combination with RabbitMQ.
It's the first time I'm working with websockets and after reading some posts about Socket.IO, Ratchet, etc. I've decided to halt on Swoole because it's written in C and handy to use with php.
This is how I understood the idea of enabling data transfer using websockets:
1) Start RabbitMQ worker and Swoole server in CLI
2) php application sends data to RabbitMQ
3) RabbitMQ sends message with data to worker
4) Worker receives message with data + establishes socket connection with Swoole socket server.
5) Swoole server broadcasts data to all connections
The question is how to bind Swoole socket server with RabbitMQ? Or how to make RabbitMQ to establish connection with Swoole and send data to it?
Here is the code:
Swoole server (swoole_sever.php)
$server = new \swoole_websocket_server("0.0.0.0", 2345, SWOOLE_BASE);
$server->on('open', function(\Swoole\Websocket\Server $server, $req)
{
echo "connection open: {$req->fd}\n";
});
$server->on('message', function($server, \Swoole\Websocket\Frame $frame)
{
echo "received message: {$frame->data}\n";
$server->push($frame->fd, json_encode(["hello", "world"]));
});
$server->on('close', function($server, $fd)
{
echo "connection close: {$fd}\n";
});
$server->start();
Worker which receives message from RabbitMQ, then makes connection to Swoole and broadcasts the message via socket connection (worker.php)
$connection = new AMQPStreamConnection('0.0.0.0', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
echo ' [*] Waiting for messages. To exit press CTRL+C', "\n";
$callback = function($msg){
echo " [x] Received ", $msg->body, "\n";
sleep(substr_count($msg->body, '.'));
echo " [x] Done", "\n";
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
// Here I'm trying to make connection to Swoole server and sernd data
$cli = new \swoole_http_client('0.0.0.0', 2345);
$cli->on('message', function ($_cli, $frame) {
var_dump($frame);
});
$cli->upgrade('/', function($cli)
{
$cli->push('This is the message to send to Swoole server');
$cli->close();
});
};
$channel->basic_qos(null, 1, null);
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);
while(count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
New task where the message will be send to RabbitMQ (new_task.php):
$connection = new AMQPStreamConnection('0.0.0.0', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('task_queue', false, true, false, false);
$data = implode(' ', array_slice($argv, 1));
if(empty($data)) $data = "Hello World!";
$msg = new AMQPMessage($data,
array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT)
);
$channel->basic_publish($msg, '', 'task_queue');
echo " [x] Sent ", $data, "\n";
$channel->close();
$connection->close();
After starting both swoole server and worker I'm triggering new_task.php from command line:
php new_task.php
In command line prompt where a RabbitMQ Worker is running (worker.php) I can see that a message is delivered to the worker ("[x] Received Hello World!" message is appearing).
However in command line prompt where Swoole server is running happens nothing.
So the questions are:
1) Is the idea of this approach right?
2) What am I doing wrong?
In the callback(in worker.php) that fires when a message is received you're using swoole_http_client which is async only. This seems to results in the code never being fully executed as the callback function returns before the async code is triggered.
A synchronous method of doing the same thing will solve the problem. Here is a simple example:
$client = new WebSocketClient('0.0.0.0', 2345);
$client->connect();
$client->send('This is the message to send to Swoole server');
$recv = $client->recv();
print_r($recv);
$client->close();
Check out the WebSocketClient class and example usage at github.
You can also wrap it in a coroutine, like this:
go(function () {
$client = new WebSocketClient('0.0.0.0', 2345);
$client->connect();
$client->send('This is the message to send to Swoole server');
$recv = $client->recv();
print_r($recv);
$client->close();
});

How to get all messages from the queue in php-amqplib (RabbitMQ)?

Currently, I'm using https://github.com/php-amqplib/php-amqplib and I've read a lot of examples in this repository but I still don't understand how to get all the messages from the queue?
I just need to receive some messages, group them by value and perform an action.
Is it possible to do with RabbitMQ at all?
How can I implement this in php?
<?php
use PhpAmqpLib\Connection\AMQPStreamConnection;
$connection = new AMQPStreamConnection('rabbitmq', 5672, 'guest', 'guest');
$channel = $connection->channel();
$queueName = 'task_queue';
$channel->queue_declare($queueName, false, true, false, false);
$result = $channel->basic_get($queueName);
var_dump($result);
$channel->close();
$connection->close();
You cannot get all messages currently available by calling single method.
Closest possible solution is by using basic_consume method.
For example:
function process_message($message)
{
echo "Received message '" . $message->body . "'\n";
/** Do your grouping here **/
}
$channel->basic_consume($queue, '', false, false, false, false, 'process_message');
// Loop as long as the channel has callbacks registered
while (count($channel->callbacks)) {
$channel->wait();
}
You can check official RabbitMQ PHP tutorial or demo from php-amqplib.

Cannot read all messages from RabbitMQ

I create RabbitMQ listener:
$connection = new AMQPConnection(
$AMQP_config['server'],
$AMQP_config['port'],
$AMQP_config['user'],
$AMQP_config['password'],
$AMQP_config['virtual_host']
);
$channel = $connection->channel();
$channel->basic_qos(
null,
1,
null
);
$channel->basic_consume(
$AMQP_config['queue'],
'',
false,
false,
false,
false,
array($this, 'CallbackResponse')
);
while(count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();
public function CallbackResponse(AMQPMessage $msg)
{
$response = json_decode($msg->body)->acopMessage;
if ($response->reqMRef == $this->_request_reference) {
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
}
}
I have 5 messages on my RabbitMQ server. But I receive only one callback, only one entering into CallbackResponse().
I want to check all messages from the queue, find the one I've sent, read it, so there will be 4 messages left.
What I doing wrong, why I receive only first message?
Using this: https://github.com/videlalvaro/php-amqplib
Your QoS is set to 1. So RabbitMQ will only send one message at a time.
As you only ack the message you are expecting for, the first message you received which does not match you condition remains unack. Thus, RabbitMQ will not send new message.
So messages that do not match must be unacked and requeued

yii-node-socket is not working for me

Recently I have been trying to integrate nodejs with my existing project to make sure of live updating of feeds. Thus, I used the following yii plugin and followed the instruction:
https://github.com/oncesk/yii-node-socket
The host and port are: bigcat,3001 respectively and I have configured them in the console.php and main.php as mentioned. I started the node server and the log indicated that it was listening to:
Listening bigcat:3001
Set origin: bigcat:*
At my client side I have the following codes (i created it as an external javascript):
// create object and connect to web socket server
var socket = new YiiNodeSocket();
socket.on('event.example', function () {
console.log('*****Trigger test.event, is a global event, for all clients*****');
});
socket.onConnect(function () {
socket.room('example').join(function (isConnected, numberOfRoomClients) {
if (isConnected) {
// catch test.event only for this rooom
console.log('***Joined into room example: members count [' + numberOfRoomClients + ']*****');
this.on('example.room.event', function (data) {
console.log('****WORKING****');
});
}
});
});
My console records that the client is successfully connected.An at homeController test method, I have the following code:
$event = Yii::app()->nodeSocket->getFrameFactory()->createEventFrame();
$event->setRoom('example');
$event->setEventName('example.room.event');
$event['type_string'] = 'hello world';
$event['type_array'] = array(1, 2, 3);
$event['type_object'] = array('one' => 1, 'two' => 2);
$event['type_bool'] = true;
$event['type_integer'] = 11;
$event->send();
When I invoke the homeController test method in another browser, my client should be able to listen to the event and the console should print "IT's WORKING". However, that is not happening. My log is showing the following error:
warn: handshake error INVALID SERVER: server host 127.0.0.1 not allowed for /server
debug: websocket writing 7::/server:undefined
The server side codes seems to not work at all. Could I get some advice on how to get it done. I feel I am close and yet far.
The server.config.js setting is:
module.exports = {
host : 'bigcat',
port : parseInt('3001'),
origin : 'bigcat:*',
allowedServers : ["127.0.1.1"],
dbOptions : {"driver":"dummy","config":[]},
checkClientOrigin : 1,
sessionVarName : 'PHPSESSID'
};
I think you need to add 127.0.0.1 in yii config file -> node socket configuration section -> parameter is allowedServerAddresses
Like this^
'nodeSocket' => array(
'class' => 'application.extensions.yii-node-socket.lib.php.NodeSocket',
'host' => 'localhost', // default is 127.0.0.1, can be ip or domain name, without http
'port' => 3001 // default is 3001, should be integer,
<b>'allowedServerAddresses' => array('127.0.0.1')</b>
)
And can you show node-socket/lib/js/server/server.config.js config file?

Categories