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();
}
}
Related
I want to load the JMS messages in a queue in real time, if is a consumer.setMessageListener(myListener) viable and how it works?
I'm not sure how a listener working. I wrote a listener like below.
public class JmsMessageListenerExample {
public static void main(String[] args) throws URISyntaxException, Exception {
BrokerService broker = BrokerFactory.createBroker(new URI(
"broker:(tcp://localhost:61616)"));
broker.start();
Connection connection = null;
try {
// Producer
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
"tcp://localhost:61616");
connection = connectionFactory.createConnection();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("customerQueue");
String payload = "Important Task";
Message msg = session.createTextMessage(payload);
MessageProducer producer = session.createProducer(queue);
System.out.println("Sending text '" + payload + "'");
producer.send(msg);
// Consumer
MessageConsumer consumer = session.createConsumer(queue);
consumer.setMessageListener(new ConsumerMessageListener("Consumer"));
connection.start();
} finally {
if (connection != null) {
connection.close();
}
broker.stop();
}
}
After I run the JmsMessageListenerExample, it completed quickly and I didn't received any messages. If a listener should keep on running until I stop it?
When you set a JMS message listener it will receive messages asynchronously in its own thread (invoked by the JMS implementation). In your case you need to prevent main from exiting and stopping your application because when that happens the MessageListener will be terminated.
Also, when you say "load the JMS messages in a queue in real time" I assume you mean "consume JMS messages from a queue as soon as possible when queue receives them." If that's the case then a JMS message listener is the right approach.
How do I configure my J2EE application so that I can invoke ActiveMQ service along with tomcat server? I am aware about embedded broker, here asking how to start the ActiveMQ whenever I start tomcat
Current Code (works fine) :
Now I want to remove main() method and use the code to run when tomcat runs.
public class JMSService {
public void produceJMS() throws NamingException, JMSException {
ConnectionFactory connFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
Connection conn = connFactory.createConnection();
conn.start();
Session session = conn.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("testQueue");
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.PERSISTENT);
TextMessage message = session.createTextMessage("Test Message ");
// send the message
producer.send(message);
System.out.println("sent: " + message);
}}
Here is my consumer :
public class JMSReceiver implements MessageListener,ExceptionListener {
public static void main(String args[]) throws Exception {
JMSReceiver re = new JMSReceiver();
re.receiveJMS();
}
public void receiveJMS() throws NamingException, JMSException {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
// Getting the queue 'testQueue'
Destination destination = session.createQueue("testQueue");
MessageConsumer consumer = session.createConsumer(destination);
// set an asynchronous message listener
JMSReceiver asyncReceiver = new JMSReceiver();
consumer.setMessageListener(asyncReceiver);
connection.setExceptionListener(asyncReceiver);
}
#Override
public void onMessage(Message message) {
System.out.println("Received message : " +message);
}
}
What #Tim Bish said is correct. You either need to have a timer say for example receiver should listen for 1 hour- or make it available until program terminate. Either case you need to start your consumer program once:
Change your receiveJMS method as follows:
public void receiveJMS() throws NamingException, JMSException {
try{
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
Connection connection = connectionFactory.createConnection();
connection.start(); // it's the start point
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
// Getting the queue 'testQueue'
Destination destination = session.createQueue("testQueue");
MessageConsumer consumer = session.createConsumer(destination);
// set an asynchronous message listener
// JMSReceiver asyncReceiver = new JMSReceiver();
//no need to create another object
consumer.setMessageListener(this);
connection.setExceptionListener(this);
// connection.close(); once this is closed consumer no longer active
Thread.sleep(60 *60 * 1000); // receive messages for 1 hour
}finally{
connection.close();// after 1 hour close it
}
}
The above program will listen upto 1 hour. If you want it as long as the program run, remove the finally block. But the recommended way is to close it somehow. since your application seems to be standalone ,you can check the java runtime shutdown hook, where you can specify how to release such resources while program terminates.
If your consumer is a web application you can close it in a ServletContextlistner.
You aren't giving the consumer application any time to actually receive a message, you create it, then you close it. You either need to use a timed receive call to do an sync receive of the message from the Queue or you need to add some sort of wait in the main method such as a CountDownLatch etc to allow the async onMessage call to trigger shutdown once processing of the message is complete.
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.
I'm trying connect from a amqp client to a aqtivemq server with default settings. It always gives the error message saying connection refused. Then I tried it with a rabbitmq server instead of a activemq server and it works fine. I wonder whether activemq needs a linux library to communicate.
Activemq server versions used which does not connects: 5.4.2 / 5.10.0
Rabitmq version used: 3.3.5
rabitmq sample client code
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel;
public class Cache {
private final static String QUEUE_NAME = "hello";
public static void main(String[] argv)
throws java.io.IOException {
//creating the connection factory
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
//Creating a connection to the server
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//declaring a queuw
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
//publishing the queue the queue
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");
//closing the connection
channel.close();
connection.close();
}
}
Fails in the following line of code
//Creating a connection to the server
Connection connection = factory.newConnection();
How can I solve this issue ?
I found a similar issue and I fixed checking the exchange declared was equals to the channel used to publish, in this way:
#Test
public void test() throws KeyManagementException, NoSuchAlgorithmException, URISyntaxException, IOException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("10.211.55.20");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("guest");
factory.setPassword("guest");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("KipcastDirect", "direct",
true, /* durable */
true, /* autodelete */
null); /* */
byte[] messageBodyBytes = "Hello, world!".getBytes();
AMQP.BasicProperties.Builder basic = new AMQP.BasicProperties.Builder();
AMQP.BasicProperties minBasic = basic.build();
minBasic = basic.priority(0).deliveryMode(1).build();
channel.basicPublish("KipcastDirect", "KipcastRouting", minBasic, messageBodyBytes);
System.out.println(" [x] Sent ");
channel.close();
}
Please be carefoul: the URI (from and to) on Camel Spring DSL context and JUnit class must refer to same Exchange and Queue to prevent a reply-text=PRECONDITION_FAILED – parameters for queue ‘QUEUE’ in vhost ‘/’ not equivalen error or similar. To check the queues / exchanges configuration parameter use:
rabbitmqadmin -V / list queue
rabbitmqadmin -V test list exchanges
Take a look to this: http://www.andreagirardi.it/blog/camel-and-rabbitmq-finally-how-to/
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) {
...