How can I handle multiple messages concurrently from a JMS topic (not queue) with java and spring 3.0?

后端 未结 9 812
無奈伤痛
無奈伤痛 2020-12-05 19:10

Note that I\'d like multiple message listeners to handle successive messages from the topic concurrently. In addition I\'d like each message listener to operate transaction

相关标签:
9条回答
  • 2020-12-05 19:48

    You don't want multiple DefaultMessageListenerContainer instances, no, but you do need to configure the DefaultMessageListenerContainer to be concurrent, using the concurrentConsumers property:

    Specify the number of concurrent consumers to create. Default is 1.

    Specifying a higher value for this setting will increase the standard level of scheduled concurrent consumers at runtime: This is effectively the minimum number of concurrent consumers which will be scheduled at any given time. This is a static setting; for dynamic scaling, consider specifying the "maxConcurrentConsumers" setting instead.

    Raising the number of concurrent consumers is recommendable in order to scale the consumption of messages coming in from a queue. However, note that any ordering guarantees are lost once multiple consumers are registered. In general, stick with 1 consumer for low-volume queues.

    However, there's big warning at the bottom:

    Do not raise the number of concurrent consumers for a topic. This would lead to concurrent consumption of the same message, which is hardly ever desirable.

    This is interesting, and makes sense when you think about it. The same would occur if you had multiple DefaultMessageListenerContainer instances.

    I think perhaps you need to rethink your design, although I'm not sure what I'd suggest. Concurrent consumption of pub/sub messages seems like a perfectly reasonable thing to do, but how to avoid getting the same message delivered to all of your consumers at the same time?

    0 讨论(0)
  • 2020-12-05 19:49

    I've run into the same problem. I'm currently investigating RabbitMQ, which seems to offer a perfect solution in a design pattern they call "work queues." More info here: http://www.rabbitmq.com/tutorials/tutorial-two-java.html

    If you're not totally tied to JMS you might look into this. There might also be a JMS to AMQP bridge, but that might start to look hacky.

    I'm having some fun (read: difficulties) getting RabbitMQ installed and running on my Mac but think I'm close to having it working, I will post back if I'm able to solve this.

    0 讨论(0)
  • 2020-12-05 19:53

    Here's a possibility:

    1) create only one DMLC configured with the bean and method to handle the incoming message. Set its concurrency to 1.

    2) Configure a task executor with its #threads equal to the concurrency you desire. Create an object pool for objects which are actually supposed to process a message. Give a reference of task executor and object pool to the bean you configured in #1. Object pool is useful if the actual message processing bean is not thread-safe.

    3) For an incoming message, the bean in DMLC creates a custom Runnable, points it to the message and the object pool, and gives it to task executor.

    4) The run method of Runnable gets a bean from the object pool and calls its 'process' method with the message given.

    #4 can be managed with a proxy and the object pool to make it easier.

    I haven't tried this solution yet, but it seems to fit the bill. Note that this solution is not as robust as EJB MDB. Spring e.g. will not discard an object from the pool if it throws a RuntimeException.

    0 讨论(0)
  • 2020-12-05 19:56

    Multiple Consumers Allowed on the Same Topic Subscription in JMS 2.0, while this was not the case with JMS 1.1. Please refer: https://www.oracle.com/technetwork/articles/java/jms2messaging-1954190.html

    0 讨论(0)
  • 2020-12-05 19:58

    Creating a custom task executor seemingly solved the issue for me, w/o duplicate processing:

    @Configuration
    class BeanConfig {
        @Bean(destroyMethod = "shutdown")
        public ThreadPoolTaskExecutor topicExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setAllowCoreThreadTimeOut(true);
            executor.setKeepAliveSeconds(300);
            executor.setCorePoolSize(4);
            executor.setQueueCapacity(0);
            executor.setThreadNamePrefix("TOPIC-");
            return executor;
        }
    
        @Bean
        JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer, @Qualifier("topicExecutor") Executor topicExecutor) {
            DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
            factory.setPubSubDomain(true);
            configurer.configure(factory, connectionFactory);
            factory.setPubSubDomain(true);
            factory.setSessionTransacted(false);
            factory.setSubscriptionDurable(false);
            factory.setTaskExecutor(topicExecutor);
            return factory;
        }
    
    }
    
    class MyBean {
        @JmsListener(destination = "MYTOPIC", containerFactory = "topicListenerFactory", concurrency = "1")
        public void receiveTopicMessage(SomeTopicMessage message) {}
    }
    
    0 讨论(0)
  • 2020-12-05 20:01

    on server.xml configs:

    so , in maxSessions you can identify the number of sessions you want.

    0 讨论(0)
提交回复
热议问题