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

后端 未结 9 813
無奈伤痛
無奈伤痛 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 20:03

    Came across this question. My configuration is :

    Create a bean with id="DefaultListenerContainer", add property name="concurrentConsumers" value="10" and property name="maxConcurrentConsumers" value ="50".

    Works fine, so far. I printed the thread id and verified that multiple threads do get created and also reused.

    0 讨论(0)
  • 2020-12-05 20:05

    At least in ActiveMQ what you want is totally supported, his name is VirtualTopic

    The concept is:

    1. You create a VirtualTopic (Simply creating a Topic using the prefix VirtualTopic. ) eg. VirtualTopic.Color
    2. Create a consumer subscribing to this VirtualTopic matching this pattern Consumer.<clientName>.VirtualTopic.<topicName> eg. Consumer.client1.VirtualTopic.Color, doing it, Activemq will create a queue with that name and that queue will subscribe to VirtualTopic.Color then every message published to this Virtual Topic will be delivered to client1 queue, note that it works like rabbitmq exchanges.
    3. You are done, now you can consume client1 queue like every queue, with many consumers, DLQ, customized redelivery policy, etc.
    4. At this point I think you understood that you can create client2, client3 and how many subscribers you want, all of them will receive a copy of the message published to VirtualTopic.Color

    Here the code

    @Component
    public class ColorReceiver {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(MailReceiver.class);
    
        @Autowired
        private JmsTemplate jmsTemplate;
    
        // simply generating data to the topic
        long id=0;
        @Scheduled(fixedDelay = 500)
        public void postMail() throws JMSException, IOException {
    
            final Color colorName = new Color[]{Color.BLUE, Color.RED, Color.WHITE}[new Random().nextInt(3)];
            final Color color = new Color(++id, colorName.getName());
            final ActiveMQObjectMessage message = new ActiveMQObjectMessage();
            message.setObject(color);
            message.setProperty("color", color.getName());
            LOGGER.info("status=color-post, color={}", color);
            jmsTemplate.convertAndSend(new ActiveMQTopic("VirtualTopic.color"), message);
        }
    
        /**
         * Listen all colors messages
         */
        @JmsListener(
            destination = "Consumer.client1.VirtualTopic.color", containerFactory = "colorContainer"
            selector = "color <> 'RED'"
        )
        public void genericReceiveMessage(Color color) throws InterruptedException {
            LOGGER.info("status=GEN-color-receiver, color={}", color);
        }
    
        /**
         * Listen only red colors messages
         *
         * the destination ClientId have not necessary exists (it means that his name can be a fancy name), the unique requirement is that
         * the containers clientId need to be different between each other
         */
        @JmsListener(
    //      destination = "Consumer.redColorContainer.VirtualTopic.color",
            destination = "Consumer.client1.VirtualTopic.color",
            containerFactory = "redColorContainer", selector = "color='RED'"
        )
        public void receiveMessage(ObjectMessage message) throws InterruptedException, JMSException {
            LOGGER.info("status=RED-color-receiver, color={}", message.getObject());
        }
    
        /**
         * Listen all colors messages
         */
        @JmsListener(
            destination = "Consumer.client2.VirtualTopic.color", containerFactory = "colorContainer"
        )
        public void genericReceiveMessage2(Color color) throws InterruptedException {
            LOGGER.info("status=GEN-color-receiver-2, color={}", color);
        }
    
    }
    
    @SpringBootApplication
    @EnableJms
    @EnableScheduling
    @Configuration
    public class Config {
    
        /**
         * Each @JmsListener declaration need a different containerFactory because ActiveMQ requires different
         * clientIds per consumer pool (as two @JmsListener above, or two application instances)
         * 
         */
        @Bean
        public JmsListenerContainerFactory<?> colorContainer(ActiveMQConnectionFactory connectionFactory, 
            DefaultJmsListenerContainerFactoryConfigurer configurer) {
    
            final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            factory.setConcurrency("1-5");
            configurer.configure(factory, connectionFactory);
            // container.setClientId("aId..."); lets spring generate a random ID
            return factory;
        }
    
        @Bean
        public JmsListenerContainerFactory<?> redColorContainer(ActiveMQConnectionFactory connectionFactory,
            DefaultJmsListenerContainerFactoryConfigurer configurer) {
    
            // necessary when post serializable objects (you can set it at application.properties)
            connectionFactory.setTrustedPackages(Arrays.asList(Color.class.getPackage().getName()));
    
            final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            factory.setConcurrency("1-2");
            configurer.configure(factory, connectionFactory);
            return factory;
        }
    
    }
    
    public class Color implements Serializable {
    
        public static final Color WHITE = new Color("WHITE");
        public static final Color BLUE = new Color("BLUE");
        public static final Color RED = new Color("RED");
    
        private String name;
        private long id;
    
        // CONSTRUCTORS, GETTERS AND SETTERS
    }
    
    0 讨论(0)
  • 2020-12-05 20:07

    This is one of those occasions where the differences in transport providers bubble up through the abstraction of JMS. JMS wants to provide a copy of the message for each subscriber on a topic. But the behavior that you want is really that of a queue. I suspect that there are other requirements driving this to a pub/sub solution which were not described - for example other things need to subscribe to the same topic independent of your app.

    If I were to do this in WebSphere MQ the solution would be to create an administrative subscription which would result in a single copy of each message on the given topic to be placed onto a queue. Then your multiple subscribers could compete for messages on that queue. This way your app could have multiple threads among which the messages are distributed, and at the same time other subscribers independent of this application could dynamically (un)subscribe to the same topic.

    Unfortunately, there's no generic JMS-portable way of doing this. You are dependent on the transport provider's implementation to a great degree. The only one of these I can speak to is WebSphere MQ but I'm sure other transports support this in one way or another and to varying degrees if you are creative.

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