How to implement the following situation on Java with RabbitMQ:
where every node, send messages to all other nodes and every node receive messages from all other nodes ?
UPDATE 1:
I tried to create the above situation with the following code:
ReceiveLogs.java
public class ReciveLogs {
...
public void start() throws IOException, TimeoutException, InterruptedException {
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(coda, false, false, false, null);
channel.exchangeDeclare(exName, BuiltinExchangeType.FANOUT);
channel.queueBind(coda, exName, "");
channel.basicPublish(exName, codaX, null, message.getBytes("UTF-8"));
System.out.println(" ReceiveLogs Sent: " + message);
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" ReceiveLogs RECEIVED:" + message);
}
};
channel.basicConsume(codaX, true, consumer);
}
}
EmitLog.java
public class EmitLog {
...
public void start() throws IOException, TimeoutException {
connection = factory.newConnection();
channel = connection.createChannel();
channel.exchangeDeclare(exName, BuiltinExchangeType.FANOUT);
channel.queueDeclare(codaX, false, false, false, null);
channel.queueBind(codaX, exName, "");
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" ROUTER Received: '" + message);
}
};
String message = channel.basicConsume(codaX, true, consumer);
channel.basicPublish(exName, "", null, message.getBytes("UTF-8"));
System.out.println("ROUTER Sent: " + message);
channel.close();
connection.close();
}
}
You can achieve this by creating a "fanout" exchange.
This is the setup you need to do:
Create a fanount exchange
Create 3 queues, one for each node. Say Q1, Q2 and Q3 corresponding to C1, C2 and C3.
Bind all queues (Q1, Q2 and Q3) to the fanount exchange created in step 1
Listening Code:
Create a listener for each of the nodes. For example, C1 node listens for messages on Queue "Q1", C2 for "Q2" and so on.
Publishing:
- Whenever you want to send a message, publish the message on broadcast exchange you created.
There is small caveat here: If C1 publishes the message, then C1 receives the same message as well. So, if you don't any node to process the same message it published, then you can use one of the attributes in the message to filter it out.
Further documentation:
https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchange-fanout
Related
I have written a code for consumer in Spring Boot RabbitMQ. After getting the message, I want to further process it. But for that, I want access to it outside the callback. I have skipped some of the irrelevant code inside Postmapping function.
#CrossOrigin(origins = "*")
#RestController
public class PlottingRabbitMQ {
private static Map<String, String> linkParams = new HashMap<>();
static Logger logger
= LoggerFactory.getLogger(LoginRabbitMQ.class);
private final static String PLOTTING_RESEND_QUEUE = "plotting_resend";
#JsonDeserialize
#PostMapping(value = "/plotting")
public Object createLink(#RequestBody PlottingModel plotting, #RequestHeader Map<String, String> plottingHeaders) throws Exception {
ConnectionFactory factory_plotting_resend = new ConnectionFactory();
factory_plotting_resend.setHost("localhost");
Connection connection_plotting_resend = factory_plotting_resend.newConnection();
Channel channel_plotting_resend = connection_plotting_resend.createChannel();
channel_plotting_resend.queueDeclare(PLOTTING_RESEND_QUEUE,
false, false,false, null);
logger.info("[!] Waiting for messages. To exit press Ctrl+C");
Consumer consumer_plotting_resend = new DefaultConsumer(channel_plotting_resend){
#Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body)
throws IOException {
String respBodyPlotting = new String(body, "UTF-8");
logger.info("[x] Message Received' " + respBodyPlotting + "'");
}
};
channel_plotting_resend.basicConsume(PLOTTING_RESEND_QUEUE, true, consumer_plotting_resend);
System.out.println("Received outside callback is: " + respBodyPlotting);
As you can see right at the bottom, I have written a system out where I want to print respBodyPlotting outside the channel_plotting_resend callback (I'm getting an error when I try to print it). In logger.info, I'm receiving the message but I'm clueless on how to getting it outside the function. Can someone please help me on this.
final BlockingQueue<String> response = new ArrayBlockingQueue<>(1);
Consumer consumer = new DefaultConsumer(channel){
#Override
public void handleDelivery(String consumerTag,
Envelope envelope,
AMQP.BasicProperties properties,
byte[] body) throws IOException {
response.offer(new String(body, StandardCharsets.UTF_8));
}
};
channel.basicConsume(QUEUE, true, consumer);
System.out.println("Received outside callback is: " + response.take());
I am trying to receive JSON messages from a Solace JMS queue but I am not receiving any message. Below is my code
#Service
public class QueueConsumer {
final String QUEUE_NAME = "test.Request.Q.V01";
// Latch used for synchronizing between threads
final CountDownLatch latch = new CountDownLatch(1);
public void run(String... args) throws Exception {
String host = "test.solace.com";
String vpnName = "TEST_VPN";
String username = "testVpn";
String password = "test123";
System.out.printf("QueueConsumer is connecting to Solace messaging at %s...%n", host);
SolConnectionFactory connectionFactory = SolJmsUtility.createConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setVPN(vpnName);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setDynamicDurables(true);
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, SupportedProperty.SOL_CLIENT_ACKNOWLEDGE);
System.out.printf("Connected to the Solace Message VPN '%s' with client username '%s'.%n", vpnName, username);
Queue queue = session.createQueue(QUEUE_NAME);
MessageConsumer messageConsumer = session.createConsumer(queue);
messageConsumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
try {
if (message instanceof SolaceMsg) {
System.out.printf("TextMessage received: '%s'%n", ((SolaceMsg) message).getClass());
} else {
System.out.println("Message received.");
}
System.out.printf("Message Content:%n%s%n", SolJmsUtility.dumpMessage(message));
message.acknowledge();
latch.countDown(); // unblock the main thread
} catch (JMSException ex) {
System.out.println("Error processing incoming message.");
ex.printStackTrace();
}
}
});
System.out.println("Start receiving messages....");
connection.start();
System.out.println("Awaiting message...");
latch.await();
connection.stop();
messageConsumer.close();
session.close();
connection.close();
}
public static void main(String... args) throws Exception {
new QueueConsumer().run(args);
}
}
My message type is JSON ad below, and I have created a POJO for this.
{
"customerDetails": {
"customerID": "0001234",
"customerName": "John"
}
}
I am getting one warning saying Response - 400 Queue already exists as it is an existing queue, and I am not receiving any messages. What am I doing wrong here?
Your code snippet looks correct. You can log on to the PubSub+ Manager of your event broker to verify that the client is binding to the correct queue and that the messages were successfully published to the queue and are waiting to be consumed. You can also enable Solace JMS API logging to understand more about what the application is doing: https://docs.solace.com/Solace-JMS-API/Code-and-Compile-Guideli.htm
I am trying to retrieve response from a rabbitmq queue, on the first go with the same code, I was able to retrieve the response correctly but whenever I run it the second or third time it does not call the delivercallback function where I am trying to retrieve the response
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received '" + message + "'");
};
boolean autoAck = true; // acknowledgment is covered below
channel.basicConsume(QUEUE_NAME, autoAck, deliverCallback, consumerTag -> { });
Expected: Response should get printed which is there in DeliverCallback lambda expression.
Actual: DeliverCallback lambda expression not getting called that is why I am not able to retrieve response from rabbitmq queue
Sending back an acknowledgement removes the message from queue, thus the message is not retrievable when consumer application is triggered for the second and the third time.
Try this:
private static void LogMsg(String msg) {
System.out.println(msg);
}
// ...
DeliverCallback deliverCallback = new DeliverCallback() {
public void handle(String s, Delivery delivery) throws IOException {
LogMsg("s - "+s);
LogMsg("Tag: "+delivery.getEnvelope().getDeliveryTag());
String message = new String(delivery.getBody(), "UTF-8");
LogMsg(message);
}
};
channel.basicConsume(queueName, true, deliverCallback, new CancelCallback() {
public void handle(String consumerTag) throws IOException {
}
});
I'm developing a Java REST API service and now I need to make a TCP connection between server and mobile devices to send message. I have found that RabbitMQ is a good idea but I'm really newbee in AMQP protocol. The question is how to send message from server to two clients that read bytes from the same queue.
My code:
public class RabitSecClient {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages2");
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received 2 '" + message + "'");
}
};
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
I execute this code twice for testing and when I send message only first client take it. What is the reason?
Hi look into below site may be you got the answer of you question. Basically RabbitMq is based on publish subscribe mechanism if you put any msg to particular queue. Any number of user got that msg if that access same queue.
https://pubs.vmware.com/vfabric52/index.jsp?topic=/com.vmware.vfabric.rabbitmq.2.8/rabbit-web-docs/tutorials/tutorial-three-java.html
I have an application which works as rabbitmq producer. I have applied RPC approach and there is no problem. Producer publishes message and consumes its response in replyQueues (temporary queue). Firstly, I have used QueueingConsumer for producer consuming and I used to set an timeout to nextDelivery(timeout) method. QueueingConsumer is deprecated now and In RabbitMQ offical site they have changed their RPC tutorial and They have used DefaultConsumer instead of QueueingConsumer. I have replaced QueueingConsumer with DefaultConsumer too. But there is a problem now: How can I set an timeout to DefaultConsumer? Because if consumer does not sent any response, trash temporary queues remains in the broker. Old and new producer consuming part is below. Thanks for your helps.
Old producer consuming approach:
consumer = new QueueingConsumer(channel);
channel.basicConsume(replyQueueName, true, consumer);
channel.basicPublish("", requestQueueName, props, message.getBytes("UTF-8"));
while (true) {
QueueingConsumer.Delivery deliver = consumer.nextDelivery(timeout);
if (deliver.getProperties().getCorrelationId().equals(corrId)) {
response = new String(deliver.getBody(), "UTF-8");
break;
}
}
return response;
new producer consuming approach:
final BlockingQueue<String> response = new ArrayBlockingQueue<String>(1);
Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
if (properties.getCorrelationId().equals(corrId)) {
response.offer(new String(body, "UTF-8"));
}
}
};
channel.basicConsume(replyQueueName, true, consumer);
return response.take();
It solved. A timeout can be set to "response" object. Changes in the "new producer consuming approach" can be as following:
Timeout to response: response.poll(5000, TimeUnit.MILLISECONDS) must be used instead of response.take().