I've set up my fanout exchange hello.fanout.
Clients connecting and want hello messages connect to rabbitmq and declear queue hello.<GUID>. New clients will create new queues with the hello prefix in the name.
In my simple mind I thought I could create binding on the hello.fanout to hello.* queues but that is not possible?
Client queue:
Trying to add binding:
How can I get this to work? My thought was to create a publisher that publishes to and exchange and have unknown amount of clients connect and listen to the messages being published as I grow my application.
RabbitMQ Exchange deliver a message to one or multiple consumers (queues). This pattern is known as "publish/subscribe".
(The queue name is not the catch here, you can use even aqueue with unknown name).
There are 4 types of exchange: fanout, direct, topic and header.
The fanout exchange is very simple. it just broadcasts all the messages it receives to all the queues that is bounded to it (no routing key need).
The relationship between exchange and a queue is called a binding.
so add a binding to all queues in the consumers.
For example:
channel.QueueBind(queue: queueName,
exchange: "hello.fanout",
routingKey: "");
Sample of code:
Publisher
//declare an exchange
channel.ExchangeDeclare(exchange: "hello.fanout", type: ExchangeType.Fanout);
//Send a message to the exchange
var message = GetMessage(args);
var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "hello.fanout",
routingKey: "",
basicProperties: null,
body: body);
Subscriber:
//declare an exchange
channel.ExchangeDeclare(exchange: "hello.fanout", type: ExchangeType.Fanout);
//bind the queue to the exchange
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queue: queueName,
exchange: "logs",
routingKey: "");
//declare a consumer event:
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
Console.WriteLine(" [x] {0}", message);
};
//add a consumer the a queue
channel.BasicConsume(queue: queueName,
autoAck: true,
consumer: consumer);
So if you want to deliver a message to multiple consumers.
You should publish a message to your exchange, and bind all your consumers to the same exchange as the publisher.
More information about exchanges and pub/sub in rabbitMQ website
Related
Am trying to consume the message from exiting queue which is of type Direct Exchange(created with the help of exchange and routing key). I have only the exchange name and routing key and not the queue name. There were support for plain Java, but there was no place where I can find it for Spring boot.
#RabbitListener
#RabbitHandler
public void consumeMessage(Object message) {
LOGGER.debug("Message Consumed.... : {}", message.toString());
}
How can I consume messages with routing key and exchange name not the queue name as #RabbitListener asks for queue.
Consumers consume from queues not exchanges. You must bind a queue to the exchange with the routing key.
EDIT
There are several ways to automatically declare a queue on the broker.
#RabbitListener(bindings =
#QueueBinding(exchange = #Exchange("myExchange"),
key = "myRk", value = #Queue("")))
public void listen(String in) {
System.out.println(in);
}
This will bind an anonymous queue (auto-delete) which will be deleted when the application is stopped.
#RabbitListener(bindings =
#QueueBinding(exchange = #Exchange("myExchange"),
key = "myRk", value = #Queue("foo")))
public void listen(String in) {
System.out.println(in);
}
Will bind a permanent queue foo to the exchange with the routing key.
You can also simply declare #Bean s for the queue, exchange and binding.
See Configuring the broker.
As I know ActiveMQ has a feature called AUTO Acknowledge that actually inform the broker that message has been received (not acknowledging the producer).
I want to know if it is possible to send acknowledgement from consumer to producer in ActiveMQ or RabbitMQ. then I want to handle the acknowledgment message in producer and if it wouldn't receive acknowledge then sending the message again to the consumer.
You want to perform a synchronous usecase over an asynchronous medium.
In RabbitMQ's case you can use RPC, as described here - https://www.rabbitmq.com/tutorials/tutorial-six-python.html
and
https://www.rabbitmq.com/direct-reply-to.html
Please notice that even authors advise to avoid it:
When in doubt avoid RPC. If you can, you should use an asynchronous pipeline - instead of RPC-like blocking, results are asynchronously pushed to a next computation stage.
RabbitMQ Java client provides auto-acking through com.rabbitmq.client.Channel.basicConsume.
At least for ActiveMQ - this is built in. You have to turn it on in activemq.xml
<policyEntry queue=">" advisoryForConsumed="true"/>
Simply listen the advisory topic for the queue you want to monitor consumed messages for. Then you can extract message id:s and what not to "tick off" outstanding requests.
For a complete end-to-end acknowledgement, I recommend something more custom. I.e. your producer-app should listen to some "response" queue that receives responses about the status of the produced message. I.e. if processing failed - you may want to know why etc..
Anyway, here is some code with a producer that also listens to acknowledgements from ActiveMQ.
public void run() throws Exception {
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:61616");
conn = cf.createConnection();
sess = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination dest = sess.createQueue("duck");
MessageConsumer mc = sess.createConsumer(AdvisorySupport.getMessageConsumedAdvisoryTopic(dest));
mc.setMessageListener(this);
conn.start();
MessageProducer mp = sess.createProducer(sess.createQueue("duck"));
mp.send(sess.createTextMessage("quack"));
}
public void onMessage(Message msg) {
try {
String msgId = msg.getStringProperty("orignalMessageId");
System.out.println("Msg: " + msgId + " consumed");
} catch ( Exception e) {
e.printStackTrace();
}
}
I'm looking to be able to bind a queue to multiple exchanges utilizing the RabbitListener annotation but so far have been unsuccessful.
What I have right now is:
#RabbitListener(bindings = #QueueBinding(value =
#Queue(
value = "${subscriber.queueInbound}", durable = "true", autoDelete = "false", exclusive = "false"),
exchange = #Exchange(value = "all", durable = "true")
),
containerFactory = "subscriberRabbitListenerContainerFactory"
)
public void onMessage(Message message, Channel channel) {
// do something
}
This will on start/re-connect auto create the queue defined as subscriber.queueInbound and bind this queue to a default all exchange.
I then have a Job that runs in the background that will then properly configure this queue and bind it to the multiple exchanges it needs to be configured for.
I'm looking for a more elegant way of doing this either through the #RabbitListener or somehow adjusting it so that upon re-connection have it configure the queue appropriately before re-listening.
Originally I was doing the queue configuration through Beans however this prevented startup of the application if RabbitMQ was not available which I resolved but would then result in it starting up and the queue configuration steps not be performed.
#RabbitListener(bindings = {
#QueueBinding(value =
#Queue(value = "foo"), exchange = #Exchange("ex1"), key="foo"),
#QueueBinding(value =
#Queue(value = "foo"), exchange = #Exchange("ex2"), key="bar")
})
public void listen(String in) {
}
Originally I was doing the queue configuration through Beans however this prevented startup of the application if RabbitMQ was not available which I resolved but would then result in it starting up and the queue configuration steps not be performed.
That implies you were doing something "illegal" during context initialization. You should not try to talk to RabbitMQ until the context is fully built.
Beans are only declared on the broker when the connection is first opened.
I'm trying to implement RabbitMQ Request-Response pattern.
My goal is that there would be always one consumer that listens to a queue and replies to messages.
So I've set the server up by:
var consumer = new EventingBasicConsumer(channel);
channel.BasicConsume(queue: 'listensToThisQueue',
noAck: true,
consumer: consumer);
consumer.Received += (model, ea) =>
{
// Replying to client
channel.BasicPublish(exchange: '',
routingKey: routingKey,
basicProperties: props,
body: message);
// Basic ack
channel.BasicAck(deliveryTag: incomingDeliveryTag,
multiple: false);
}
Everything actually works fine:
Client sends first message
Server receive and reply
Client gets the message
The only problem is that after finshing Received method (After executing BasicAck) - Consumer stops listening to listenToQueue (I actually see 0 consumers on this queue in the UI management).
Why is that ?
This is happening because you are subscribing to the queue with noAck set to True. In this case, the consumer crashed after the first message was delivered (which is why you see 0 consumers). So you have to subscribe to the queue with noAck set to False.
I am able to receive the message only when the receiver is running before of the sender.
But I need to be able to send a message while the receiver is not running.
And get it when the receiver starts.
Is it possible?
c# example:
//Send
Channel.ExchangeDeclare(ExchangeName,"direct");
Channel.BasicPublish(ExchangeName, "MyRoute", null, body);
//Receive (other app)
Channel.ExchangeDeclare(ExchangeName,"direct");
String queueName = Channel.QueueDeclare().QueueName;
Channel.QueueBind(queueName, ExchangeName, "MyRoute");
QueueingBasicConsumer consumer = new QueueingBasicConsumer(Channel);
Channel.BasicConsume(queueName, false, consumer);
BasicDeliverEventArgs Message=consumer.Queue.DequeueNoWait(null);
you are creating an auto-delete queue:
String queueName = Channel.QueueDeclare().QueueName;
so when you close the consumer, the queue is deleted automatically.
if you want do that, you have to create an durable queue, than bind the queue to the Exchange, like this:
channel.queueDeclare("yourQueue", true, false, false, null);
In this way you don't need the consumer up and running. If the consumer is down the messages will be stored to the queue.