Java 如何在两个应用程序之间发布/订阅JMS消息?
我有两个Java独立应用程序。我希望从一个应用程序发送消息,并通过两个客户端异步接收消息:一个客户端与发送方位于同一个应用程序中。另一个在不同的应用程序中。两者都与ActiveMQ代理连接。但我只能看到第一个客户收到了消息,而另一个客户没有收到消息。通过JMS连接两个应用程序的一般方法是什么?我想我一定对JMS有一些不清楚的概念。我四处查看,但不知道如何设置Springbean配置文件,以便在两个Java应用程序之间发布/订阅消息 这是第一个应用程序中我的发送者的bean配置文件,也是同一应用程序中的第一个侦听器bean:Java 如何在两个应用程序之间发布/订阅JMS消息?,java,spring,jms,activemq,Java,Spring,Jms,Activemq,我有两个Java独立应用程序。我希望从一个应用程序发送消息,并通过两个客户端异步接收消息:一个客户端与发送方位于同一个应用程序中。另一个在不同的应用程序中。两者都与ActiveMQ代理连接。但我只能看到第一个客户收到了消息,而另一个客户没有收到消息。通过JMS连接两个应用程序的一般方法是什么?我想我一定对JMS有一些不清楚的概念。我四处查看,但不知道如何设置Springbean配置文件,以便在两个Java应用程序之间发布/订阅消息 这是第一个应用程序中我的发送者的bean配置文件,也是同一应用程
<bean id="customerMessageSender" class="com.example.message.CustomerStatusSender">
<property name="jmsTemplate" ref="jsmTemplateBean" />
<property name="topic" ref="topicBean" />
</bean>
<bean id="jsmTemplateBean" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactoryBean"/>
<property name="pubSubDomain" value="true"/>
</bean>
<bean id="topicBean" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="CustomerStatusTopic" />
</bean>
<bean id="connectionFactoryBean" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
<bean id="customerStatusListener" class="com.example.message.CustomerStatusListener" />
<bean id="listenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactoryBean" />
<property name="destination" ref="topicBean" />
<property name="messageListener" ref="customerStatusListener" />
</bean>
<bean id="topicBean" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="CustomerStatusTopic" />
</bean>
<bean id="connectionFactoryBean" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
<bean id="anotherCustomerStatusListener" class="com.mydomain.jms.CustomerStatusMessageListener" />
<bean id="listenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactoryBean" />
<property name="destination" ref="topicBean" />
<property name="messageListener" ref="anotherCustomerStatusListener" />
</bean>
再次编辑:以下是第二个应用程序中与第一个应用程序分离的第二个侦听器。它包含一个main
方法来实例化侦听器bean(jms-beans.xml是上面列出的第二个侦听器的bean配置文件)。
<bean id="anotherCustomerStatusListener" class="com.mydomain.jms.CustomerStatusMessageListener" />
public class CustomerStatusMessageListener implements MessageListener {
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
System.out.println("Subscriber 2 got you! The message is: "
+ ((TextMessage) message).getText());
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type TextMessage");
}
}
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("jms-beans.xml");
CustomerStatusMessageListener messageListener = (CustomerStatusMessageListener) context.getBean("anotherCustomerStatusListener");
context.close();
}
}
您对JMS的理解是正确的。如果您希望两个侦听器接收相同的消息,则可以使用一个主题。如果一个侦听器与发送器在同一个VM中运行,而另一个则不在,这应该无关紧要。在看不到代码的情况下,Spring配置看起来也是正确的。这就留下了一些需要检查的问题:
- 两个侦听器是否在同一台主机上运行?也就是说,
是一个侦听器的位置,而另一个侦听器的位置不同吗localhost
- 在发送消息时,您的第二个侦听器是否正在运行?如果您的第二个侦听器在发送消息时未处于活动状态,则它不会看到消息是否稍后启动,除非您使用的是持久主题,并且您的订阅者至少连接到代理一次
<property name="subscriptionDurable" value="true">
<property name="clientId" value="Some_unique_id">
<property name="durableSubscriptionName" value="Some_unique_id">
每个订阅服务器的客户端id和持久订阅名称必须不同,因此您的第一个侦听器将具有以下内容:
<bean id="listenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactoryBean" />
<property name="destination" ref="topicBean" />
<property name="messageListener" ref="anotherCustomerStatusListener" />
<property name="subscriptionDurable" value="true">
<property name="clientId" value="listener1">
<property name="durableSubscriptionName" value="listener1">
</bean>
第二,应具备:
<bean id="listenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactoryBean" />
<property name="destination" ref="topicBean" />
<property name="messageListener" ref="anotherCustomerStatusListener" />
<property name="subscriptionDurable" value="true">
<property name="clientId" value="listener2">
<property name="durableSubscriptionName" value="listener2">
</bean>
请注意,必须至少启动第二个侦听器一次才能向代理注册自己,以便代理知道其clientId并为其存储消息,但您可以关闭它,稍后再启动它,以获取它关闭时丢失的任何消息
如果您的侦听器在大容量系统上长时间处于停机状态,代理将存储它的所有消息,这最终可能会填满磁盘或减慢代理的速度。请参阅。谢谢您的建议。对于您指出的第一项,两个侦听器都运行在同一主机和同一JVM上。对于第二项,当第一个应用程序发送消息时,第二个侦听器未运行,即,当第一个应用程序将消息发送到ActiveMQ主题时,第二个应用程序未处于活动状态。那么,如何让第二个侦听器在消息启动后检索消息呢?我最初的想法是:JMS允许任何消息侦听器在离线一段时间后获取消息。这是真的吗?我运行这两个应用程序的方式也是:在这两个应用程序中有两个
static main()
方法。我运行了第一个main()。然后我运行了第二个main()
,但是第二个侦听器没有收到消息。我是否应该将这两个应用程序放在同一个web服务器上,让服务器充当消息发送/接收的触发点,而不是使用两个main()
方法作为独立Java应用程序运行这两个应用程序?听起来您需要持久的主题。我已经用有关在ActiveMQ中设置持久主题的信息更新了答案。无论您决定将代码作为独立java应用程序运行还是在服务器中运行,只要这两种解决方案没有延长停机时间,JMS容器都不会在意。您应该让系统的其他需求驱动体系结构的这一部分。我怀疑侦听器是否有足够的时间接收消息,甚至可能没有足够的时间向ActiveMQ注册。在关闭spring上下文并退出main之前,请尝试让第二个侦听器保持较长时间的活动状态,只需几十秒左右的Thread.sleep()
。延迟的最可能原因是设置从侦听器到代理的初始连接的时间,加上代理在发送回消息之前所做的任何设置和验证。一旦建立了初始连接,消息生成和消息使用之间的延迟就会减少。ActiveMQ在与您类似的情况下每秒声明2000条消息。看见