我正在尝试使用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.myTopic
和ActiveMQ.Advisory.Producer.Queue.myTopic
,并且“myTopic”确实出现在队列页面(即/queues.jsp)。
节点按以下顺序启动:
创建的第一个“主题”是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.myTopic
和ActiveMQ.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");
}
连线作为适配器的目的地,而不仅仅是一个字符串。
再次感谢。
答案 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();
}