RabbitMQ: Routing key + queue + delay - java

I have a Producer as follows:
public class MyProducer {
private static final String EXCHANGE_NAME = "messages";
public static void main(String[] argv)
throws java.io.IOException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String color1 = "red"
String message1 = "message1"
String color2 = "blue"
String message2 = "message2"
channel.basicPublish(EXCHANGE_NAME, color1, null, message1);
channel.basicPublish(EXCHANGE_NAME, color2, null, message2);
channel.close();
connection.close();
}
}
and also a consumer:
public class MyConsumer {
private static final String EXCHANGE_NAME = "messages";
public static void main(String[] argv)
throws java.io.IOException,
java.lang.InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, "color1");
channel.queueBind(queueName, EXCHANGE_NAME, "color2");
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer);
}
}
My questions are now:
Do I have now only one queue named "queuName" or do I have two queues named "color1" and "color2"?
I don't want to consume the messages immediatly. So what I want is to set a delay for each queue "color1" and "color2". How can I achieve this?

Question-1) Do I have now only one queue named "queuName" or do I have two queues named "color1" and "color2"?
Answer : You have to must go through tutorial
https://www.rabbitmq.com/getstarted.html
base on that you decide how you want to create queue and which exchange types[direct, topic, headers and fanout] match to your requirement or sometime its happen no need to exchange ..so first see tutorial and then base on your requirement decide.
Question-2)I don't want to consume the messages immediately. So what I want is to set a delay for each queue "color1" and "color2". How can I achieve this?
Answer: for that you have to write your own logic which delay the consumer to find message from rabbit, you can go through thread also.
Enjoy Rabbit programming :)

For your first question, the answer is "neither." Your code shows you declaring a randomly-named queue and assigning whatever the server names it to the variable queueName:
String queueName = channel.queueDeclare().getQueue();
Your uses of "color1" and "color2" are as binding keys (see this page) on the random queue you created. If the intent is to declare a queue of a specific name, I believe that would need to be passed in as an argument to the queueDeclare function (though admittedly I am unfamiliar with this particular library).
For your second question, if you don't want to consume messages immediately, then you don't have to. Instead, initiate the consumer when you want to. Nobody is forcing you to put that in your program directly beneath the queueDeclare method. I cannot list specific ways to achieve this as there are probably as many as you can think of.

Related

RabbitMQ Java Consumer app best practice for Ubuntu linux deployment

I'm studing how RabbitMQ works with Java, it is almost clear how Producer works and how to implement the Consumer, but I'm still not sure how to deploy the Consumer application to server with a correct threading handling.
Let's say I have a web-application that has to sent some transaction email as producer, this application will push in the queue the messages and it is managed by a container, like Tomcat, that increase/descrease threads to serve multiple requests.
What is the best practice to deployt the consumer application?
I have read many tutorials and the RabbitMQ commands are well explained, usually this is the code (from official RabbitMq Hello World):
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.nio.charset.StandardCharsets;
public class Recv {
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 messages. To exit press CTRL+C");
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
System.out.println(" [x] Received '" + message + "'");
};
channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });
}
}
However it is not clear to me how I should run this application in my server (sure I can start it by command line!) and how am I sure the process will not crash for some reason?
Should I put the channel opening commands in a loop inside a try/catch to be sure it will not exit?
Am I'm in charge of handling thread pool opening/increasing?
Does exists something for this purpose?

Netty 4.0.23 multiple hosts single client

My question is about creating multiple TCP clients to multiple hosts using the same event loop group in Netty 4.0.23 Final, I must admit that I don't quite understand Netty 4's client threading business, especially with the loads of confusing references to Netty 3.X.X implementations I hit through my research on the internet.
with the following code, I establish a connection with a single server, and send random commands using a command queue:
public class TCPsocket {
private static final CircularFifoQueue CommandQueue = new CircularFifoQueue(20);
private final EventLoopGroup workerGroup;
private final TcpClientInitializer tcpHandlerInit; // all handlers shearable
public TCPsocket() {
workerGroup = new NioEventLoopGroup();
tcpHandlerInit = new TcpClientInitializer();
}
public void connect(String host, int port) throws InterruptedException {
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.remoteAddress(host, port);
b.handler(tcpHandlerInit);
Channel ch = b.connect().sync().channel();
ChannelFuture writeCommand = null;
for (;;) {
if (!CommandQueue.isEmpty()) {
writeCommand = ch.writeAndFlush(CommandExecute()); // commandExecute() fetches a command form the commandQueue and encodes it into a byte array
}
if (CommandQueue.isFull()) { // this will never happen ... or should never happen
ch.closeFuture().sync();
break;
}
}
if (writeCommand != null) {
writeCommand.sync();
}
} finally {
workerGroup.shutdownGracefully();
}
}
public static void main(String args[]) throws InterruptedException {
TCPsocket socket = new TCPsocket();
socket.connect("192.168.0.1", 2101);
}
}
in addition to executing commands off of the command queue, this client keeps receiving periodic responses from the serve as a response to an initial command that is sent as soon as the channel becomes active, in one of the registered handlers (in TCPClientInitializer implementation), I have:
#Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(firstMessage);
System.out.println("sent first message\n");
}
which activates a feature in the connected-to server, triggering a periodic packet that is returned from the server through the life span of my application.
The problem comes when I try to use this same setup to connect to multiple servers,
by looping through a string array of known server IPs:
public static void main(String args[]) throws InterruptedException {
String[] hosts = new String[]{"192.168.0.2", "192.168.0.4", "192.168.0.5"};
TCPsocket socket = new TCPsocket();
for (String host : hosts) {
socket.connect(host, 2101);
}
}
once the first connection is established, and the server (192.168.0.2) starts sending the designated periodic packets, no other connection is attempted, which (I think) is the result of the main thread waiting on the connection to die, hence never running the second iteration of the for loop, the discussion in this question leads me to think that the connection process is started in a separate thread, allowing the main thread to continue executing, but that's not what I see here, So what is actually happening? And how would I go about implementing multiple hosts connections using the same client in Netty 4.0.23 Final?
Thanks in advance

Consumer is not receiving message from MQ when message is sent before consumer is listening

I am using MQs for the first time and attempting to implement a logging system with RabbitMQ. My implementation involves a 'sender'
/*
* This class sends messages over MQ
*/
public class MQSender {
private final static String EXCHANGE_NAME = "mm_exchange";
private final static String[] LOG_LEVELS = {"green", "orange", "red", "black"};
public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
/*
* Boilerplate stuff
*/
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//declare the exchange that messages pass through, type=direct
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String[] levels = {"green", "orange", "red", "black"};
for (String log_level : levels) {
String message = "This is a " + log_level + " message";
System.out.println("Sending " + log_level + " message");
//publish the message with each of the bindings in levels
channel.basicPublish(EXCHANGE_NAME, log_level, null, message.getBytes());
}
channel.close();
connection.close();
}
}
Which sends one message for each of my colors to the exchange, where the color will be used as bindings. And it involves a 'receiver'
public class MQReceiver {
private final static String EXCHANGE_NAME = "mm_exchange";
private final static String[] LOG_LEVELS = {"green", "orange", "red", "black"};
public static void main(String[] args) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
receiveMessagesFromQueue(2);
}
public static void receiveMessagesFromQueue(int maxLevel) throws IOException, ShutdownSignalException, ConsumerCancelledException, InterruptedException {
/*
* Boilerplate stuff
*/
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//declare the exchange that messages pass through, type=direct
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//generate random queue
String queueName = channel.queueDeclare().getQueue();
//set bindings from 0 to maxLevel for the queue
for (int level = 0; level <= maxLevel; level++) {
channel.queueBind(queueName, EXCHANGE_NAME, LOG_LEVELS[level]);
}
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer);
while(true) {
//waits until a message is delivered then gets that message
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
String routingKey = delivery.getEnvelope().getRoutingKey();
System.out.println(" [x] Received '" + routingKey + "':'" + message + "'");
}
}
}
which is given as a parameter a number representing which color bindings I would like it to be fed from the exchange.
In my implementation, and in RabbitMQ in general, it seems like messages are stored in the exchange until the Consumer asks for them, at which point they are distributed to their respective queues and then sent one at a time to the client (or consumer in MQ lingo). My problem is that when I run the MQSender class before running the MQReceiver class the messages never get delivered. But when I run the MQReceiver class first the messages are received. From my understanding of MQ I would think that the messages should be stored on the server until the MQReceiver class is run, then the messages should be delivered to their consumers, however this is not what is happening. My main question is whether these messages can be stored in an exchange and if not, where should they be stored so that they will be delivered once a consumer (i.e. my MQReceiver class) is called?
Thanks for your help!
RabbitMQ discards messages if their routing key doesn't match any queues bound to the exchange. When you start MQSender first, no queues are bound, so the messages it sends are lost. When you start MQReceiver, it binds queues to the exchange, so RabbitMQ has a place to put the message from MQSender. When you stop MQReceiver, since you created an anonymous queue, the queue and all bindings are removed from the exchange.
If you want messages to be stored on the server while MQReceiver is not running, you need the receiver to create a named queue, and bind the routing keys to that queue. Note that creating a named queue is idempotent, and the queue won't be created if it already exists. Then you need the receiver to pull messages off the named queue.
Change your code to look something like this:
MQSender
....
String namedQueue = "logqueue";
//declare named queue and bind log level routing keys to it.
//RabbitMQ will put messages with matching routing keys in this queue
channel.queueDeclare(namedQueue, false, false, false, null);
for (int level = 0; level < LOG_LEVELS.length; level++) {
channel.queueBind(namedQueue, EXCHANGE_NAME, LOG_LEVELS[level]);
}
...
MQReceiver
...
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
QueueingConsumer consumer = new QueueingConsumer(channel);
//Consume messages off named queue instead of anonymous queue
String namedQueue = "logqueue";
channel.basicConsume(namedQueue, true, consumer);
while(true) {
...

messages not even appears on the RabbitMQ server

In my RabbitMQ app the producer sends messages, but consumer receives only part of them(not the biggest one). when I execite rabbitmqctl list_queues in terminal it shows:
~$ sudo rabbitmqctl list_queues
Listing queues ...
MdnaMessagesQueue 0
...done.
which means that messages are lost going to the queue or they were taken from the queue very fast. so I can't understand why this happens. I have no other apps which could get messages from that queue. here is my producers code:
public class Sender {
private static final String QUEUE_NAME = "MdnaMessagesQueue";
public void send(byte[] message) throws IOException {
AMQP.BasicProperties.Builder bob = new AMQP.BasicProperties.Builder();
AMQP.BasicProperties minBasic = bob.build();
AMQP.BasicProperties minPersistentBasic = bob.deliveryMode(2).build();
ExecutorService service = Executors.newFixedThreadPool(10);
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection(service);
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.basicPublish("", QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message);
channel.close();
connection.close();
}
}

test with Jmeter for consumer and producer

I have two programs, the first is a producer:
public class Producer {
public static void main(String[] args) throws Exception {
final ConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:12345");
final Connection c = cf.createConnection();
final Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
final TextMessage msg = s.createTextMessage("rho " + new Date().toString());
final MessageProducer p = s.createProducer(new ActiveMQQueue("rmannibucau"));
p.send(msg);
p.close();
s.close();
c.close();
}
}
and the second is a consumer:
public class Listener {
public static void main(String[] args) throws Exception {
final ConnectionFactory cf = new ActiveMQConnectionFactory("tcp://localhost:12345");
final Connection c = cf.createConnection();
final Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
s.createConsumer(s.createQueue("rmannibucau")).setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
System.out.println(message.toString());
}
});
c.start();
s.run();
}
}
I use ActiveMQ, java, jms and I want to test with Jmetter to know how much message consumer can consume in 1 minute, help me please.
You're going to need to create some sort of Custom Java Sampler. When creating one of these, you can call your code from inside, set start/end timers, if the request was successful/failed etc. Then, you put this jar into JMeter and it will appear as a Java Sampler. From here, you can use all of JMeter's functionality to specify users, requests, time limit, etc.
I hope this is enough of a start to help you out.

Categories