2

我有两个 Java 独立应用程序。我想从一个应用程序发送消息并由两个客户端异步接收消息:一个与发送者在同一个应用程序中。另一个在不同的应用程序中。两者都与 ActiveMQ 代理连接。但我只能看到第一个客户端收到了消息,而另一个没有收到消息。通过 JMS 连接两个应用程序的一般方法是什么?我想我对 JMS 一定有一些不清楚的概念。我四处张望,但不知道如何设置我的 Spring bean 配置文件以在两个 Java 应用程序之间发布/订阅消息。

这是我在第一个应用程序中的发送者的 bean 配置文件,也是同一个应用程序中的第一个侦听器的 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 配置文件:

<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> 

如您所见,customerStatusListenerbean 和anotherCustomerStatusListenerbean 都订阅topicBean. 但是只有第一个侦听器会收到消息,因为它与发送者在同一个应用程序中,而第二个侦听器没有。通过 JMS 连接两个 Java 应用程序以便可以在两个单独的应用程序之间发送/接收消息的一般原则是什么?

编辑:我无法在第一个 XML 文件中添加以下侦听器 bean,因为该类CustomerStatusMessageListener位于不同的应用程序中,因此在第一个(发送者的)应用程序的类路径中不可见。

<bean id="anotherCustomerStatusListener" class="com.mydomain.jms.CustomerStatusMessageListener" />

再次编辑:以下是第二个应用程序中的第二个侦听器,它与第一个应用程序分开。它包含一个main实例化监听器 bean 的方法(jms-beans.xml 是上面列出的第二个监听器的 bean 配置文件)。

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();
    }
}
4

1 回答 1

6

您对 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 并为其存储消息,但您可以将其关闭并稍后启动它以获取它错过的任何消息下。

如果您的侦听器在大容量系统上长时间处于停机状态,代理将为其存储所有消息,这最终可能会填满磁盘或减慢代理速度。请参阅ActiveMQ 文档以了解自动删除持久消息

于 2013-11-10T05:10:23.033 回答