package bo.gotthardt.queue.rabbitmq; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Stopwatch; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.Envelope; import io.dropwizard.jackson.Jackson; import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.function.Function; /** * A RabbitMQ {@link com.rabbitmq.client.Consumer} that maps messages to a specific type and runs a processing function on them. * * @author Bo Gotthardt */ @Slf4j class FunctionConsumer<T> extends DefaultConsumer { private static final ObjectMapper MAPPER = Jackson.newObjectMapper(); private final Function<T, Void> processor; private final Class<T> type; private final Meter successCount; private final Timer successDuration; private final Meter failureCount; private final Timer failureDuration; FunctionConsumer(Channel channel, Function<T, Void> processor, Class<T> type, String name, MetricRegistry metrics) { super(channel); this.processor = processor; this.type = type; String metricPrefix = "queue." + type.getSimpleName() + "." + name + ".consume"; this.successCount = metrics.meter(MetricRegistry.name(metricPrefix, "success", "count")); this.successDuration = metrics.timer(MetricRegistry.name(metricPrefix, "success", "duration")); this.failureCount = metrics.meter(MetricRegistry.name(metricPrefix, "failure", "count")); this.failureDuration = metrics.timer(MetricRegistry.name(metricPrefix, "failure", "duration")); } @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { long deliveryTag = envelope.getDeliveryTag(); Stopwatch stopwatch = Stopwatch.createStarted(); try { T message = MAPPER.readValue(body, type); if (log.isTraceEnabled()) { log.trace("Received message '{}' with data '{}'.", deliveryTag, new String(body)); } processor.apply(message); getChannel().basicAck(deliveryTag, false); stopwatch.stop(); successCount.mark(); successDuration.update(stopwatch.elapsed(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS); log.info("Processed {} message '{}' succesfully in {} ms.", type.getSimpleName(), deliveryTag, stopwatch.elapsed(TimeUnit.MILLISECONDS)); } catch (Exception e) { getChannel().basicNack(deliveryTag, false, true); stopwatch.stop(); failureCount.mark(); failureDuration.update(stopwatch.elapsed(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS); log.error("Processing {} message '{}' failed with exception:", type.getSimpleName(), deliveryTag, e); } } }