#Override
public void onMessage(final Message message) {
this.handleThreadPool.execute(new Runnable() {
public void run() {
try {
MultiThreadMessageListener.this.messageHandler.handle(message);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
I want to know whether activemq will send the message again when throw RuntimeException ?
It appears you are handling the Message in a Thread inside your own executor so the answer would be, Nope. Once onMessage returns the Message would be acknowledge as consumed if you are using auto acknowledge mode for instance.
Related
I am trying to log any information or exception that occurs during message sending in RabbitMQ, for that I tried to add ConnectionListener on the existing connection factory.
kRabbitTemplate.getConnectionFactory().addConnectionListener(new ConnectionListener() {
#Override
public void onCreate(Connection connection) {
System.out.println("Connection Created");
}
#Override
public void onShutDown(ShutdownSignalException signal) {
System.out.println("Connection Shutdown "+signal.getMessage());
}
});
kRabbitTemplate.convertAndSend(exchange, routingkey, empDTO);
To test the exception scenario, I unbind and even deleted the queue from RabbitMQ console. But I did not get any exception or any shutdown method call.
Although, When I stopped RabbitMQ service, I got
Exception in thread "Thread-5" org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
But this exception is not from the listener I added.
I want to know
Why I did not get any exception or call from shutdown method
How can I use ConnectionListner and/or ChannelListner for logging failure/success of message delivery.
Can we use the AMQP appender, if yes how can we do that? (any example / tutorial)
What are the other approaches to ensure the message is sent?
Note: I do not want to use the publisher confirm the approach.
Connection Refused is not a ShutdownSignalException - the connection was never established because the broker is not present on the server/port.
You can't use the listeners to confirm delivery or return of individual messages; use publisher confirms and returns for that.
https://docs.spring.io/spring-amqp/docs/current/reference/html/#publishing-is-async
See the documentation for how to use the appenders.
https://docs.spring.io/spring-amqp/docs/current/reference/html/#logging
EDIT
To get notified of failures to connect, you currently need to use other techniques, depending on whether you are sending or receiving.
Here is an example that shows how:
#SpringBootApplication
public class So66882099Application {
private static final Logger log = LoggerFactory.getLogger(So66882099Application.class);
public static void main(String[] args) {
SpringApplication.run(So66882099Application.class, args);
}
#RabbitListener(queues = "foo")
void listen(String in) {
}
// consumer side listeners for no connection
#EventListener
void consumerFailed(ListenerContainerConsumerFailedEvent event) {
log.error(event + " via event listener");
if (event.getThrowable() instanceof AmqpConnectException) {
log.error("Broker down?");
}
}
// or
#Bean
ApplicationListener<ListenerContainerConsumerFailedEvent> eventListener() {
return event -> log.error(event + " via application listener");
}
// producer side - use a RetryListener
#Bean
RabbitTemplate template(ConnectionFactory cf) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(cf);
RetryTemplate retry = new RetryTemplate();
// configure retries here as needed
retry.registerListener(new RetryListener() {
#Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
return true;
}
#Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable) {
log.error("Send failed " + throwable.getMessage());
}
#Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable) {
}
});
rabbitTemplate.setRetryTemplate(retry);
return rabbitTemplate;
}
#Bean
public ApplicationRunner runner(RabbitTemplate template) {
return args -> {
try {
template.convertAndSend("foo", "bar");
}
catch (Exception e) {
e.printStackTrace();
}
};
}
}
I'm trying to understand if this is a feature or a bug... :-)
For the below controller and exception mapper, for a rest client that will fail with a 401 response, I would expect the exception handler to be invoked for both cases. However it's not invoked for the WebApplicationException. Why is that and how are you meant to register an exception handler for them cases. This is using Quarkus version 0.21.2
#Path("/failable")
public class FailableResource {
#Inject
#RestClient
private SomeHttpClient httpClient;
#GET
#Path("fails")
#Produces(MediaType.TEXT_PLAIN)
public String fails() {
try {
return httpClient.someQuery();
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
#GET
#Path("works")
#Produces(MediaType.TEXT_PLAIN)
public String works() {
try {
return httpClient.someQuery();
} catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException("Not a WebApplicationException");
}
}
}
And the ExceptionMapper
#Provider
public class HandleMySillyError implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable e) {
e.printStackTrace();
return Response.ok("Some handled response").build();
}
}
I found out when running in quarkus:dev mode the exception mapper is not invoked. It seems that this is caused by an exception mapper from quarkus that is only used in DEV mode (see https://github.com/quarkusio/quarkus/issues/7883).
I launched my code local as normal a normal java program, causing my exception handler to work as expected. Also when running the code on Openshift, my custom exception mapper is used as well.
note: I used quarkus version 1.8.3
I am new to spring-amqp. I am trying to manually acknowledge the messages instead of using auto-ack.
I am seeing that the last message is being unacked in the management console.
image for unacked message in managemnet console.
but the queue is empty.
As soon as I stop the server the last message gets acknowledged. How do I handle this and how can I print in logs ,the message id/information which has been unacknowledged..
Here is the code which I have implemented.
RabbitConfig.java:
public class RabbitMQConfig {
final static String queueName = "spring-boot";
#Bean
Queue queue() {
return new Queue(queueName, true,false,false,null);
}
#Bean
TopicExchange exchange() {
return new TopicExchange("spring-boot-exchange");
}
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(queueName);
}
#Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setMessageListener(listenerAdapter);
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return container;
}
#Bean
Consumer receiver() {
return new Consumer();
}
#Bean
MessageListenerAdapter listenerAdapter(Consumer receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
Consumer.java
public class Consumer implements ChannelAwareMessageListener{
#RabbitListener(queues = "spring-boot")
public void receiveMessage(String message, Channel channel, #Header(AmqpHeaders.DELIVERY_TAG) long tag)
throws IOException, InterruptedException {
Thread.sleep(500);
channel.basicAck(tag, true);
System.out.println(tag + "received");
}
#Override
public void onMessage(Message arg0, Channel arg1) throws Exception {
// TODO Auto-generated method stub
}
Producer endpoints:
#RestController
public class HelloController {
private final RabbitTemplate rabbitTemplate;
public HelloController(RabbitTemplate rabbitTemplate) {
this.rabbitTemplate = rabbitTemplate;
}
// Call this end point from the postman or the browser then check in the
// rabbitmq server
#GetMapping(path = "/hello")
public String sayHello() throws InterruptedException {
// Producer operation
for (int i = 0; i < 100; i++) {
Thread.sleep(500);
rabbitTemplate.convertAndSend(RabbitMQConfig.queueName, "Hello World");
}
return "hello";
}
#GetMapping(path = "/hellotwo")
public String sayHellotwo() throws InterruptedException {
// Producer operation
for (int i = 0; i < 50; i++) {
Thread.sleep(500);
rabbitTemplate.convertAndSend(RabbitMQConfig.queueName, "SEcond message");
}
return "hellotwo";
}
You have two listener containers; the container bean and one created by the framework for the #RabbitListener.
I am not entirely sure what's happening without running a test myself, but I suspect the problem is your attempt to call receiveMessage from the simple MessageListenerAdapter.
That adapter is only designed to call a method with one argument (converted from the Message). Also, that adapter doesn't know how to map #Header parameters. I suspect that delivery fails and since you are using MANUAL acks, no more deliveries are attempted to that container because of the unack'd delivery and the default qos (1).
You don't need your container bean; instead configure the message listener container factory to set the ack mode. See the documentation.
If you are new to spring-amqp; why do you think you need manual acks? The default mode (auto) means the container will ack/nack for you (NONE is traditional rabbit auto-ack). It is not common to use manual acks with Spring.
I have a RestEasy based service in which I am doing some cleanup work in a ContainerResponseFilter. The problem is that if an unknown runtime exception (i.e. an exception for which I do not have a mapper) is thrown by a resource, the ContainerResponseFilter is never executed.
Is this the expected behavior? Is there a workaround to this? I was looking at the following question (answer by Jonas):
How should I log uncaught exceptions in my RESTful JAX-RS web service?
and that made it seem like the ContainerResponseFilter is executed even when an exception is thrown in the controller?
Am I missing something?
Didn't work for me either. This claims it should work: https://github.com/Graylog2/graylog2-server/issues/1826
I didn't want to investigate further, and simply use a plain old javax.servlet.Filter, but of course there it's hard to set the reponses-headers (after chain.doFilter(), ... grr..
So used a Spring solution:
public static class MyFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
response.setHeader("MyHeader", "MyValue");
filterChain.doFilter(request, response);
}
}
Base on the source code of SynchronousDispatcher in RestEasy.
protected void writeResponse(HttpRequest request, HttpResponse response, Response jaxrsResponse) {
try {
ServerResponseWriter.writeNomapResponse((BuiltResponse)jaxrsResponse, request, response, this.providerFactory);
} catch (Exception var5) {
this.writeException(request, response, var5);
}
}
public void writeException(HttpRequest request, HttpResponse response, Throwable e) {
if (response.isCommitted()) {
throw new UnhandledException("Response is committed, can't handle exception", e);
} else {
Response handledResponse = (new ExceptionHandler(this.providerFactory, this.unwrappedExceptions)).handleException(request, e);
if (handledResponse == null) {
throw new UnhandledException(e);
} else {
try {
ServerResponseWriter.writeNomapResponse((BuiltResponse)handledResponse, request, response, this.providerFactory);
} catch (Exception var6) {
throw new UnhandledException(var6);
}
}
}
}
ContainerResponseFilter will not execute.
If you want to set headers when exceptions happen, you need an exception handler to deal with it.
I use RabbitMQ for connection between parts my program. Version of RMQ(3.3.5). It used with java client from repo.
// Connection part
#Inject
public AMQService(RabbitMQConfig mqConfig) throws IOException {
this.mqConfig = mqConfig;
connectionFactory.setHost(mqConfig.getRABBIT_HOST());
connectionFactory.setUsername(mqConfig.getRABBIT_USERNAME());
connectionFactory.setPassword(mqConfig.getRABBIT_PASSWORD());
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setPort(mqConfig.getRABBIT_PORT());
connectionFactory.setVirtualHost(mqConfig.getRABBIT_VHOST());
Connection connection = connectionFactory.newConnection();
channel = connection.createChannel();
channel.basicQos(1);
}
//Consume part
private static void consumeResultQueue() {
final QueueingConsumer consumer = new QueueingConsumer(channel);
Future resultQueue = EXECUTOR_SERVICE.submit((Callable<Object>) () -> {
channel.basicConsume("resultQueue", true, consumer);
while (true) {
try {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody(), "UTF-8");
resultListener.onMessage(message);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
});
}
I want leave use inifinty loop. Can RMQ notify client while message can read from queue? Without check?
You can create a class which extends DefaultConsumer and override handleDelivery.
public class MyConsumer extends DefaultConsumer {
public MyConsumer(Channel channel) {
super(channel);
}
#Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
// do your computation
}
}
And register this consumer with channel.basicConsume(queueName, myConsumerInstance);
Note that by doing this, handleDelivery will run inside rabbitmq client thread pool so you should avoid any long computation inside this function.