RabbitMQ 3.5 and Message Priority Queue process is waiting until the task completion - rabbitmq

I have some priority based tasks in my application. I have two queues "queue1" and "queue2", i want to gave highest priority to queue1 and lowest priority to queue2. I setup queue1 priority number is 255 and queue2 priority number is 200. I am having an issue in executing the tasks , once priority task is taken , The process is waiting until the task completion synchronously. But as per our project need, this process shouldn't be waiting but just kick off it. How do I achieve it?
I refereed this blog,
Message Received part:
final CountDownLatch latch = new CountDownLatch(3);
ch.basicConsume(QUEUE, true, new DefaultConsumer(ch) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException {
System.out.println("Received " + new String(body));
latch.countDown();
}
});
latch.await();
I already done this process without priority queue in RabbitMQ https://pamlesleylu.wordpress.com/2013/02/02/hello-world-for-spring-amqp-and-rabbitmq/
Producer
public void execute() {
System.out.println("execute...");
messageQueue.convertAndSend("hello " + counter.incrementAndGet());
}
Consumer
public void onMessage(Message msg) {
System.out.println(new String(msg.getBody()));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

You need to hand the task off to another thread (e.g. using a TaskExecutor).
Bear in mind, though, that the message will be acked so if the server crashes you will lose that message.
It would probably be better to increase the concurrency in the listener container so you can handle multiple messages at once.

Related

rabbitmq: channel level exception recovery

I am working on a worker service that consume from rabbitmq queue and I am trying to figure out how to handle channel shut down event, for example: lets say my consumer didn't ack the broker for 30 minutes and the broker shut down the channel for that.
I know the rabbitmq clinet library (I am using the C# library) will automatically try to re-connect on connection shut down, but what is the best practice for when the connection is alive but the channel was closed? I can register handler for the 'Channel Shut Down' event but what should I do inside this handler except for logging it? I want to keep consuming from the the relevant queue after all.
here is my code, i tried to create the channel again but i get timeout exception for that:
var consumer = ...
channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);
channel.BasicQos(0, 100, false);
channel.ModelShutdown += (sender, args) =>
{
try
{
Log.Error($"channel was shut down");
channel = _connection.CreateModel();
channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);
channel.BasicQos(0, 100, false)
}
catch (Exception exception)
{
Log.Error(exception);
}
As far as I understand, the problem is related to the fact that the event processing occurs in the same thread that is trying to access the RabbitMQ server, or there is a lock blocking the creation of the channel. The simplest solution I've found is to just use Task.Run(() => )
private async void RecreateChannel(object sender, ShutdownEventArgs e)
{
_logger.LogWarning($"Channel was shutdowned, whit reason: {e.ReplyText}, code: {e.ReplyCode}, trying to reconnect");
_channel.Dispose();
while (!_channel.IsOpen)
{
try
{
await Task.Run(() => _channel = InitChannel());
}
catch (Exception exception)
{
_logger.LogError(exception, "Failed to recreate channel, trying again");
}
}
_logger.LogInformation("Channel was recreated successfully");
}

RabbitMQ Camel Consumer - Consume a single message

I have a scenario where I want to "pull" messages of a RabbitMQ queue/topic and process them one at a time.
Specifically if there are already messages sitting on the queue when the consumer starts up.
I have tried the following with no success (meaning, each of these options reads the queue until it is either empty or until another thread closes the context).
1.Stopping route immediately it is first processed
final CamelContext context = new DefaultCamelContext();
try {
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
RouteDefinition route = from("rabbitmq:harley?queue=IN&declare=false&autoDelete=false&hostname=localhost&portNumber=5672");
route.process(new Processor() {
Thread stopThread;
#Override
public void process(final Exchange exchange) throws Exception {
String name = exchange.getIn().getHeader(Exchange.FILE_NAME_ONLY, String.class);
String body = exchange.getIn().getBody(String.class);
// Doo some stuff
routeComplete[0] = true;
if (stopThread == null) {
stopThread = new Thread() {
#Override
public void run() {
try {
((DefaultCamelContext)exchange.getContext()).stopRoute("RabbitRoute");
} catch (Exception e) {}
}
};
}
stopThread.start();
}
});
}
});
context.start();
while(!routeComplete[0].booleanValue())
Thread.sleep(100);
context.stop();
}
Similar to 1 but using a latch rather than a while loop and sleep.
Using a PollingConsumer
final CamelContext context = new DefaultCamelContext();
context.start();
Endpoint re = context.getEndpoint(srcRoute);
re.start();
try {
PollingConsumer consumer = re.createPollingConsumer();
consumer.start();
Exchange exchange = consumer.receive();
String bb = exchange.getIn().getBody(String.class);
consumer.stop();
} catch(Exception e){
String mm = e.getMessage();
}
Using a ConsumerTemplate() - code similar to above.
I have also tried enabling preFetch and setting the max number of exchanges to 1.
None of these appear to work, if there are 3 messages on the queue, all are read before I am able to stop the route.
If I were to use the standard RabbitMQ Java API I would use a basicGet() call which lets me read a single message, but for other reasons I would prefer to use a Camel consumer.
Has anyone successfully been able to process a single message on a queue that holds multiple messages using a Camel RabbitMQ Consumer?
Thanks.
This is not the primary intention of the component as its for continued received. But I have created a ticket to look into supporting a basicGet (single receive). There is a new spring based rabbitmq component coming in 3.8 onwards so its going to be implemeneted there (first): https://issues.apache.org/jira/browse/CAMEL-16048

Camel Route Losing Message on restart in camel rabbitmq

I am using camel-rabbitmq.
Here is my route defination
camelContext.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("rabbitmq:TEST?queue=TEST&concurrentConsumers=5")
.routeId("jms")
.autoStartup(false)
.throttle(10)
.asyncDelayed()
.log("Consuming message ${body} to ${header.deliveryAddress}")
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println(atomicLong.decrementAndGet());
}
})
;
}
});
When I push 500 messages to this queue , when stop and start route all message on channel will be lost ,wonder where they are going.
If I configure same route with &autoAck=false it is working properly but losing performance. Why camel not offering same behavior with and without autoAck.
I managed my problem doing following change in rabbitmqconsumer of camel-rabbitmq
public void handleCancelOk(String consumerTag) {
// no work to do
log.info("Received cancelOk signal on the rabbitMQ channel");
**downLatch.countDown();**
}
#Override
protected void doStop() throws Exception {
if (channel == null) {
return;
}
this.requeueChannel=openChannel(consumer.getConnection());
if (tag != null && isChannelOpen()) {
channel.basicCancel(tag);
}
stopping=true;
downLatch.await();
try {
lock.acquire();
if (isChannelOpen()) {
channel.close();
}
} catch (TimeoutException e) {
log.error("Timeout occured");
throw e;
} catch (InterruptedException e1) {
log.error("Thread Interrupted!");
} finally {
lock.release();
}
}
By doing this camel route will for message to consumed and avoided message loss.
You need to check rabbitmq consumer prefetch count
consumer prefetch
I think By default consumer picks all the messages in queue to its memory buffers.
If you set the prefetch count to 1, consumer will acknowledge messages one by one.
All the other unacknowledged will be present in the queue in ready state. Waiting to be picked up, after the consumer completes it task on the previous message picked.

Timeout of basicPublish when server is outofspace

My case is rabbitmq server got out of space, just as below
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/ramonubuntu--vg-root 6299376 5956336 0 100% /
The producer publishes message to server(the message needs to be persisted), and then will be blocked forever, it will keeping waiting the response of publishing. Sure we should avoid the situation of server out of space, but is there any timeout mechanism to let producer quit the waiting?
I have tried heartbeat and SO_TIMEOUT, they both don't work, as the network works fine. Below is my producer.
protected void publish(byte[] message) throws Exception {
// ConnectionFactory can be reused between threads.
ConnectionFactory factory = new SoTimeoutConnectionFactory();
factory.setHost(this.getHost());
factory.setVirtualHost("te");
factory.setPort(5672);
factory.setUsername("amqp");
factory.setPassword("amqp");
factory.setConnectionTimeout(10 * 1000);
// doesn't help if server got out of space
factory.setRequestedHeartbeat(1);
final Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// declare a 'topic' type of exchange
channel.exchangeDeclare(this.exchangeName, "topic", true);
channel.addReturnListener(new ReturnListener() {
#Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey,
AMQP.BasicProperties properties, byte[] body) throws IOException {
logger.warn("[X]Returned message(replyCode:" + replyCode + ",replyText:" + replyText
+ ",exchange:" + exchange + ",routingKey:" + routingKey + ",body:" + new String(body));
}
});
channel.confirmSelect();
channel.addConfirmListener(new ConfirmListener() {
#Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
logger.info("Ack: " + deliveryTag);
// RabbitMessagePublishMain.this.release(connection);
}
#Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
logger.info("Nack: " + deliveryTag);
// RabbitMessagePublishMain.this.release(connection);
}
});
channel.basicPublish(this.exchangeName, RabbitMessageConsumerMain.EXCHANGE_NAME + ".-1", true,
MessageProperties.PERSISTENT_BASIC, message);
channel.waitForConfirmsOrDie(10*1000);
// now we can close connection
connection.close();
}
It will block at 'channel.waitForConfirmsOrDie(10*1000);', and the SotimeoutConnectionFactory,
public class SoTimeoutConnectionFactory extends ConnectionFactory {
#Override
protected void configureSocket(Socket socket) throws IOException {
super.configureSocket(socket);
socket.setSoTimeout(10 * 1000);
}
}
Also I captured the network between producer and rabbimq,
Please help.
You need to implement Connection Block/Unblocked.
This is basically a way of notifying the publisher that the server is running out of resources. The advantage with this is that the publisher will also be notified once it is safe to publish again.
I would recommend that you take a look at this article. A simple way of implementing this is to have a flag that indicates if it is safe to publish, if it is not wait until it is.
As an example you can take a look on how I implemented this in one of my Python examples.

Using exclusive + durable queues, for RabbitMQ

If I have made a queue which is exclusive and durable (not auto-delete). Now, if the consumer subscribes to that queue and then it goes down. Then that queue gets deleted.
I have checked the scenario, when the queue is only durable (i.e. neither exclusive nor auto-delete). Now, if the consumer subscribes to that queue and then it goes down. Then that queue gets deleted.
Please explain the 1st case, 2nd case is giving expected result. In both the scenario only 1 consumer is subscribed to one queue, and there is only one queue bound to one direct_exchange.
If you have a queue that is exclusive, then when the channel that declared the queue is closed, the queue is deleted.
If you have a queue that is auto-deleted, then when there are no subscriptions left on that queue it will be deleted.
These two rules apply even for durable queues.
One thing to correct, the exclusive queue will be deleted after the connection is closed not the channel is closed. you can run this test:
package rabbitmq.java.sample.exclusivequeue;
import java.io.IOException;
import com.rabbitmq.client.*;
import com.rabbitmq.client.AMQP.Queue.DeclareOk;
public class Producer {
private final static String QUEUE_NAME = "UserLogin2";
private final static String EXCHANGE_NAME = "user.login";
/**
* #param args
*/
public static void main(String[] args) {
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("CNCDS108");
try {
Connection conn = factory.newConnection();
Channel channel =conn.createChannel();
DeclareOk declareOk = channel.queueDeclare(QUEUE_NAME, false, true, false, null);
channel.basicPublish("", QUEUE_NAME, null, "Hello".getBytes());
//close the channel, check if the queue is deleted
System.out.println("Try to close channel");
channel.close();
System.out.println("Channel closed");
System.out.println("Create a new channel");
Channel channel2 =conn.createChannel();
DeclareOk declareOk2 = channel2.queueDeclarePassive(QUEUE_NAME);
**//we can access the exclusive queue from another channel
System.out.println(declareOk2.getQueue()); //will output "UserLogin2"
channel2.basicPublish("", QUEUE_NAME, null, "Hello2".getBytes());
System.out.println("Message published through the new channel");**
// System.out.println("Try to close Connection");
// conn.close();
// System.out.println("Connection closed");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}