Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在两个应用程序之间发布/订阅JMS消息?_Java_Spring_Jms_Activemq - Fatal编程技术网

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

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

我有两个Java独立应用程序。我希望从一个应用程序发送消息,并通过两个客户端异步接收消息:一个客户端与发送方位于同一个应用程序中。另一个在不同的应用程序中。两者都与ActiveMQ代理连接。但我只能看到第一个客户收到了消息,而另一个客户没有收到消息。通过JMS连接两个应用程序的一般方法是什么?我想我一定对JMS有一些不清楚的概念。我四处查看,但不知道如何设置Springbean配置文件,以便在两个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 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条消息。看见