1

I have a @RabbitListener and properly working consuming from queue. In my listener I set context.

I know that there are many (configurable) threads consuming the same queue (so in real executing the same lisener code).

Now, I consdier when is good time to clear context ?

To be more precisely, it is problem for me to express:

"When you end consuming (no matter with exception or not) do clear context)"

Below, you can see a scheme of my problem:

public class CustomContextHolder {

   private static final ThreadLocal<DatabaseType> contextHolder = 
            new ThreadLocal<DatabaseType>();

   public static void setContext(DatabaseType databaseType) {
      contextHolder.set(databaseType);
   }

   public static CustomerType getContext() {
      return (CustomerType) contextHolder.get();
   }

   public static void clearContext() {
      contextHolder.remove();
   }
}


@RabbitListener
void listenMessage(Message message) {
  ...
  CustomContextHolder.set(...);

}
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • I think that since your consumers may actually be asynchronous, there isn't a "working-everywhere" answer to your question. In general, I would think that if you need this behavior, you would structure your application in such way that all consumers are intercepted and "in-before" you set the context, and "in-after" you clear it. – M. Prokhorov Apr 03 '17 at 13:06

1 Answers1

1

It's generally better to use stateless objects as listeners rather than using ThreadLocal.

However, if you are forced to keep state this way, you can implement ApplicationListener<AmqpEvent>.

The ApplicationListener will receive several events; specifically, the AsyncConsumerStoppedEvent when the consumer is stopped normally and ListenerContainerConsumerFailedEvent if the consumer stops abnormally. These two events are published on the listener thread, so you can clear your ThreadLocal there.

Other events may or may not be published on the listener thread.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • could you consider my question according to this thread: http://stackoverflow.com/questions/43101776/context-of-class-with-listener-spring-rabbitmq-internals-and-injecting-beans/43102286?noredirect=1#comment73378784_43102286 It is why I must use ThreadLocal, but I know that it is not elegant –  Apr 03 '17 at 13:15
  • In that case (if the context changes for each message) you need to clear at the end of the listener method (perhaps in a finally block). I was assuming you wanted to hold it for the life of the thread. – Gary Russell Apr 03 '17 at 13:29
  • I must hold context due to that each message requires some operations on database. Choice of database depends on message. DataSource router choose on context, so for each message context must be set. The second thing is that also for request (depends on user ) I set context (so database to make it possible for datasource router choose datasource). So you suggest finally in listener method, alternatively implement two listener: `AsyncConsumerStoppedEvent` and `ListenerContainerConsumerFailedEvent`. Can I read somewhere about listeners (I would lke to understand when threads are executed...) –  Apr 03 '17 at 14:07
  • If the context needs to change for each message then the event solution won't work for you. So ignore that. – Gary Russell Apr 03 '17 at 15:55
  • Ok, it is good that I explain you why I need this context. So `try-finally {clearContext}` in `@RabbitListener` is only possibiilty for me. Does `spring-rabbitmq` create thread per message ? Or some pool of threads which service message in loop? –  Apr 03 '17 at 17:43
  • can I do it in ExceptionHandler ? Seems to be ok. –  Apr 03 '17 at 19:17
  • Yes, but then you won't clear it on a successful case - `finally` will handle both situations. Threads are dedicated for the life of the consumer. One thread per `concurrentConsumer`, not one thread per message. The thread dies when the container is stopped or the consumer restarted (e.g. because of a network problem). – Gary Russell Apr 03 '17 at 20:22