All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.leanix.dropkit.amqp.QueueConsumer Maven / Gradle / Ivy

There is a newer version: 2.0.10
Show newest version
package net.leanix.dropkit.amqp;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.AlreadyClosedException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.ShutdownSignalException;
import java.io.IOException;
import java.nio.charset.Charset;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Designed to consume from a single queue (holds the result from basicConsume
 * as registeredConsumerTag).
 */
public abstract class QueueConsumer extends DefaultConsumer {

    private final Logger log = LoggerFactory.getLogger(QueueConsumer.class);
    private final String queueName;
    private final ConsumerRegistry registry;

    private String registeredConsumerTag;

    private volatile boolean unregistering = false;

    private volatile long lastUsed;

    private final Charset utf8 = Charset.forName("UTF-8");

    /**
     * This method processes the message which comes from rabbitMQ and contains the business logic for each consumer.
     * 

* In case that any exception is thrown within this message, the corresponding message will NOT be acknowledged in rabbitMQ * and therefore {@link #simpleHandle(String)} will be called with the same message again. * Only in case that the exception is a {@linkplain JsonMappingException}, the message will be acknowledge in rabbitMQ and will * not processed again. *

* So, please avoid throwing exceptions here, because this can end in dead loops in case of unprocessable messages. * * @param body The pure message in json format which comes from rabbitMQ * @throws IOException An exception which causes that the message will NOT be acknowledge in rabbitMQ */ public abstract void simpleHandle(String body) throws IOException; public QueueConsumer(String queueName, Channel channel, ConsumerRegistry registry) { super(channel); this.queueName = queueName; this.registry = registry; this.lastUsed = System.currentTimeMillis(); } public String getQueueName() { return queueName; } public String getRegisteredConsumerTag() { return registeredConsumerTag; } public void setRegisteredConsumerTag(String consumerTag) { this.registeredConsumerTag = consumerTag; } public void setUnregistering() { unregistering = true; } public long getLastUsed() { return lastUsed; } @Override public void handleDelivery( String consumerTag, Envelope env, BasicProperties props, byte[] body) throws IOException { // we receive a delivery from a single thread only (no concurrency) log.debug("received message {} from queue {}", props.getMessageId(), queueName); lastUsed = System.currentTimeMillis(); String stringBody = new String(body, utf8); long startTime = System.currentTimeMillis(); try { simpleHandle(stringBody); } catch (JsonMappingException e) { // the format of previous persisted messages can not be read with this version of code log.warn("can not handle message: {}", stringBody); } // try to acknowledge the message, which could be fails in case of long running processing long duration = System.currentTimeMillis() - startTime; log.debug("finished message in time: {}", DurationFormatUtils.formatDurationWords(duration, true, false)); try { getChannel().basicAck(env.getDeliveryTag(), false); } catch (AlreadyClosedException e) { if (duration >= ConsumerRegistry.QUEUE_X_EXPIRY_MILLIS) { // raw: I assume we get sometimes the exception: // "com.rabbitmq.client.AlreadyClosedException: channel is already closed due to clean channel shutdown" because // the processing of the messages takes to long and rabbitMQ has removed the queue in the meanwhile. log.info("Received expected exception {} during long processing message, which takes: {}", e.getClass().getSimpleName(), DurationFormatUtils.formatDurationWords(duration, true, false)); } else { throw e; } } } @Override public void handleConsumeOk(String consumerTag) { log.info("consumer started consuming for queue {}, consumerTag={}", queueName, consumerTag); } @Override public void handleCancelOk(String consumerTag) { log.info("consumer for queue {}, consumerTag={}, was regularly cancelled. unregister it.", queueName, consumerTag); if (!unregistering) { unregistering = true; registry.unregister(this); } } @Override public void handleCancel(String consumerTag) { log.info("consumer for queue {}, consumerTag={}, was cancelled, e.g. because queue was deleted. unregister it.", queueName, consumerTag); if (!unregistering) { unregistering = true; registry.unregister(this); } } @Override public void handleShutdownSignal(String consumerTag, ShutdownSignalException e) { log.info("{} for consumer for queue {}, consumerTag={}, was closed. unregister consumer.", e.isHardError() ? "connection" : "channel", queueName, consumerTag); if (!unregistering) { unregistering = true; registry.unregister(this); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy