Spring Integration JMS创建ActiveMQ队列而不是主题

时间:2018-02-23 12:02:37

标签: java spring-integration activemq spring-jms spring-messaging

我正在尝试使用ActiveMQ代理向两个使用Spring Integration工具监听自动主题的消费者发送消息。

以下是我的配置bean(发布者和订阅者之间的共同点):

@Value("${spring.activemq.broker-url}")
String brokerUrl;

@Value("${spring.activemq.user}")
String userName;

@Value("${spring.activemq.password}")
String password;

@Bean
public ConnectionFactory connectionFactory() {
    ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
    connectionFactory.setBrokerURL(brokerUrl);
    connectionFactory.setUserName(userName);
    connectionFactory.setPassword(password);
    return connectionFactory;
}

@Bean
public JmsListenerContainerFactory<?> jsaFactory(ConnectionFactory connectionFactory,
        DefaultJmsListenerContainerFactoryConfigurer configurer) {
    DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
    factory.setPubSubDomain(true); //!!
    configurer.configure(factory, connectionFactory);
    return factory;
}

@Bean
public JmsTemplate jmsTemplate() {
    JmsTemplate template = new JmsTemplate();
    template.setConnectionFactory(connectionFactory());
    template.setPubSubDomain(true); //!!
    return template;
}

以下是消费者的豆类:

@Bean(name = "jmsInputChannel")
public MessageChannel jmsInputChannel() {
    return new PublishSubscribeChannel();
}

@Bean(name = "jmsInputFlow")
public IntegrationFlow buildReceiverFlow() {        
    return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(connectionFactory()).destination("myTopic"))
            .channel("jmsInputChannel").get();
}

//Consumes the message.
@ServiceActivator(inputChannel="jmsInputChannel")
public void receive(String msg){
    System.out.println("Received Message: " + msg);
}

这些是制作人的豆子:

@Bean(name = "jmsOutputChannel")
public MessageChannel jmsOutputChannel() {
    return new PublishSubscribeChannel();
}

@Bean(name = "jmsOutputFlow")
public IntegrationFlow jmsOutputFlow() {
    return IntegrationFlows.from(jmsOutputChannel()).handle(Jms.outboundAdapter(connectionFactory())
            .destination("myTopic")
    ).get();
}



private static int counter = 1;

@Scheduled(initialDelay=5000, fixedDelay=2000)
public void send() {
     String s = "Message number " + counter;
     counter++;
     jmsOutputChannel().send(MessageBuilder.withPayload(s).build());
}

我没有使用嵌入式ActiveMQ代理。我在他们自己的(码头工人)容器中使用一个经纪人,一个生产者和两个消费者。

我的问题是,虽然我在setPubSubDomain(true)JmsListenerContainerFactory都调用了JmsTemplate,但我的“主题”表现为队列:一个消费者打印所有偶数编号的消息,而另一个打印所有奇数编号。

事实上,通过访问ActiveMQ Web界面,我看到我的“主题”(即在/topics.jsp页面下)被命名为ActiveMQ.Advisory.Consumer.Queue.myTopicActiveMQ.Advisory.Producer.Queue.myTopic,并且“myTopic”确实出现在队列页面(即/queues.jsp)。

节点按以下顺序启动:

  • AMQ经纪人
  • 消费者1
  • 消费者2
  • 生产者

创建的第一个“主题”是ActiveMQ.Advisory.Consumer.Queue.myTopic,而生产者只在生产者启动后出现,显然。

我不是ActiveMQ的专家,所以也许我的生产者/消费者“主题”被命名为“.Queue”的事实只是误导。但是,我确实得到official ActiveMQ documentation中描述的队列中的语义,而不是主题。

我已经查看了this question,但是我所有的频道都已经是PublishSubscribeChannel类型。

我需要实现的是将所有消息发送给我的所有(可能是&lt; 2)消费者。

更新:我忘了提及,我的application.properties文件已包含spring.jms.pub-sub-domain=true,以及其他设置。

另外,我使用的Spring Integration版本是4.3.12.RELEASE。

问题是,我仍然得到RR负载均衡的语义而不是发布 - 订阅语义。 至于我在@Hassen Bennour提供的link中可以看到的内容,我希望在所有主题列表中获得ActiveMQ.Advisory.Producer.Topic.myTopicActiveMQ.Advisory.Consumer.Topic.myTopic行。不知怎的,我认为我没有很好地使用Spring Integration库,因此当我想设置一个主题时,我正在设置一个Queue。

更新2 :对此感到抱歉。 jmsOutputChannel2实际上jmsOutputChannel在这里,我编辑了主要部分。我在我的代码中使用辅助“主题”作为检查,生产者向自己发送消息并接收回复。 “主题”名称也有所不同,所以......它完全在一个单独的流程中。

我通过这种方式改变接收器流程确实取得了一些进展:

@Bean(name = "jmsInputFlow")
public IntegrationFlow buildReceiverFlow() {        
    //return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(connectionFactory()).destination("myTopic"))
            //.channel("jmsInputChannel").get();
return IntegrationFlows.from(Jms.publishSubscribeChannel(connectionFactory()).destination("myTopic")) //Jms.publishSubscribeChannel() rather than Jms.messageDrivenChannelAdapter()
            .channel("jmsInputChannel").get();
}

这会在代理上生成类型为Consumer.Topic.myTopic而非Consumer.Queue.myTopic的咨询主题,实际上是一个名为myTopic的主题(我可以从主题选项卡中看到)。但是,一旦生产者启动,就会创建一个Producer.Queue咨询主题,并在未交付时将消息发送到那里。

输入流中适配器的选择似乎决定了什么样的咨询消费者主题被创建(从Jms.publishSubscribeChannel()切换到Jms.messageDrivenChannelAdapter()时的主题与队列)。但是,我还没有找到类似于输出流的东西。

更新3 :问题解决了,感谢@Hassen Bennour。回顾一下:

我在制作人的jmsTemplate()

中连接了Jms.outboundAdapter()
@Bean(name = "jmsOutputFlow")
public IntegrationFlow jmsOutputFlow() {
    return IntegrationFlows.from(jmsOutputChannel()).handle(Jms.outboundAdapter(jsaTemplate())
            .destination("myTopic")
    ).get();
}

消费者Jms.messageDrivenChannelAdapter()的更复杂的配置:

@Bean(name = "jmsInputFlow")
public IntegrationFlow buildReceiverFlow() {        
    return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(
        Jms.container(connectionFactory(),"myTopic")
        .pubSubDomain(true).get()) )
        .channel("jmsInputChannel").get();
}

虽然这个可能是最顺畅,最灵活的方法,但有这样的豆......

@Bean
public Topic topic() {
    return new ActiveMQTopic("myTopic");
}

连线作为适配器的目的地,而不仅仅是一个字符串。

再次感谢。

1 个答案:

答案 0 :(得分:3)

将spring.jms.pub-sub-domain = true添加到application.properties

db.collection_name.aggregate(
     {$unwind: "$data"},
     {$unwind: "$data.fields"},
{$sort : {"data.fields.value" : -1}},
{$group: {_id:"$_id", fields: {$push:"$data.fields"}}})

@Bean public JmsListenerContainerFactory<?> jsaFactory(ConnectionFactory connectionFactory, DefaultJmsListenerContainerFactoryConfigurer configurer) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); // the configurer will use PubSubDomain from application.properties if defined or false if not //so setting it on the factory level need to be set after this configurer.configure(factory, connectionFactory); factory.setPubSubDomain(true); return factory; } 是名为myTopic的队列的咨询主题 看看这里阅读有关咨询 http://activemq.apache.org/advisory-message.html

更新:

更新您的定义,如下所示

ActiveMQ.Advisory.Consumer.Queue.myTopic

或将Destination定义为主题并按目的地替换目标(“myTopic”)(topic())

@Bean(name = "jmsOutputFlow")
public IntegrationFlow jmsOutputFlow() {
    return IntegrationFlows.from(jmsOutputChannel()).handle(Jms.outboundAdapter(jmsTemplate())
            .destination("myTopic")
    ).get();
}

@Bean(name = "jmsInputFlow")
public IntegrationFlow buildReceiverFlow() {        
    return IntegrationFlows.from(Jms.messageDrivenChannelAdapter(
            Jms.container(connectionFactory(),"myTopic")
            .pubSubDomain(true).get()) )
            .channel("jmsInputChannel").get();
}