繁体   English   中英

如何在两个应用程序之间发布/订阅JMS消息?

[英]How to publish/subscribe JMS message between two applications?

我有两个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> 

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

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

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

再次编辑:以下是第二个应用程序中的第二个侦听器,它与第一个应用程序是分开的。 它包含一个实例化侦听器bean的main方法(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();
    }
}

您对JMS的理解是正确的。 如果您希望两个侦听器都收到相同的消息,则可以通过主题来实现。 一个侦听器是否与发送者在同一虚拟机上运行而另一个侦听器不在同一虚拟机上运行则无关紧要。 没有看到您的代码,您的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文档以自动删除持久消息

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM