【问题标题】:JMS Topic Subscriber in Spring using JMS template/ Message SubscriberSpring中使用JMS模板/消息订阅者的JMS主题订阅者
【发布时间】:2018-08-14 07:53:27
【问题描述】:

我有一个使用 ActiveMQ 的 JMS Producer/Subscriber 的简单 Spring 应用程序,配置如下:

应用程序上下文 xml:

<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
    <property name="brokerURL" value="tcp://localhost:61616" />
    <property name="userName" value="user" />
    <property name="password" value="password" />
</bean>
<bean id="messageDestination" class="org.apache.activemq.command.ActiveMQTopic">
    <constructor-arg value="messageQueue1" />
</bean>

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE">
    </property>
</bean>

<bean id="springJmsProducer" class="SpringJmsProducer">
    <property name="destination" ref="messageDestination" />
    <property name="jmsTemplate" ref="jmsTemplate" />
</bean>

<bean id="springJmsConsumer" class="SpringJmsConsumer">
    <property name="destination" ref="messageDestination" />
    <property name="jmsTemplate" ref="jmsTemplate" />
</bean>

下面是Spring生产者

public class SpringJmsProducer {
private JmsTemplate jmsTemplate;
private Destination destination;

public JmsTemplate getJmsTemplate() {
    return jmsTemplate;
}

public void setJmsTemplate(JmsTemplate jmsTemplate) {
    this.jmsTemplate = jmsTemplate;
}

public Destination getDestination() {
    return destination;
}

public void setDestination(Destination destination) {
    this.destination = destination;
}

public void sendMessage(final String msg) {
    jmsTemplate.send(destination, new MessageCreator() {
        public Message createMessage(Session session) throws JMSException {
            return session.createTextMessage(msg);
        }});        
 }
}

下面是 Spring Consumer:

public class SpringJmsConsumer {
private JmsTemplate jmsTemplate;
private Destination destination;

public JmsTemplate getJmsTemplate() {
    return jmsTemplate;
}

public void setJmsTemplate(JmsTemplate jmsTemplate) {
    this.jmsTemplate = jmsTemplate;
}

public Destination getDestination() {
    return destination;
}

public void setDestination(Destination destination) {
    this.destination = destination;
}

public String receiveMessage() throws JMSException {
    TextMessage textMessage =(TextMessage) jmsTemplate.receive(destination);        
    return textMessage.getText();
 }
}

问题:当我启动生产者并发布消息,然后我启动消费者时,消费者不是在阅读旧消息,而是只阅读消费者启动后发布的消息。谁能帮助我如何制作这个持久订阅者,以便消费者读取队列中未确认的消息,并且我需要实现同步消费者而不是异步。

我已经尝试了所有可能的解决方案,但没有一个有效。任何帮助都非常感谢

【问题讨论】:

  • 我还需要实现同步消费者而不是异步。 ??当然 ?或相反,因为您的实际消费者是同步的
  • 是的同步,即我提到同步,因为我在谷歌上找到的大多数解决方案都是异步的。
  • 查看我的更新答案
  • 我已经尝试过您的更新答案以及第一点,但仍然面临同样的问题。不知道确切的问题在哪里。任何其他方法或修复?

标签: spring jms activemq jmstemplate


【解决方案1】:

如果您希望消费者在开始之前接收发送到该主题的消息,您有两种选择:

1.使用 Activemq 追溯消费者

背景 追溯消费者只是一个普通的 JMS 主题消费者 谁表示在订阅开始时,每次尝试都应该 用于及时返回并发送任何旧消息(或最后一条消息) 消费者可能错过的关于该主题的消息)。

有关详细信息,请参阅订阅恢复政策。

您将消费者标记为具有追溯性,如下所示:

topic = new ActiveMQTopic("TEST.Topic?consumer.retroactive=true");

http://activemq.apache.org/retroactive-consumer.html

2。使用持久订阅者:

请注意,Durable Subscriber 在第 2 次运行开始之前会收到发送到该主题的消息

http://activemq.apache.org/manage-durable-subscribers.html

这可以通过 DefaultMessageListenerContainer 异步实现

<bean id="jmsContainer" destroy-method="shutdown"
    class="org.springframework.jms.listener.DefaultMessageListenerContainer" >
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="destination" ref="messageDestination" />
    <property name="messageListener" ref="messageListenerAdapter" />
    <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
    <property name="subscriptionDurable" value="true" />
    <property name="clientId" value="UniqueClientId" />
</bean>

<bean id="messageListenerAdapter"
    class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
    <constructor-arg ref="springJmsConsumer" />
</bean>
<bean id="springJmsConsumer" class="SpringJmsConsumer">
</bean>

并更新您的消费者:

public class SpringJmsConsumer implements javax.jms.MessageListener {

    public void onMessage(javax.jms.Message message) {
        // treat message;
        message.acknowledge();
    }
}

更新使用

如果你想要一个同步持久订阅者,一个例子

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicSubscriber;

public class SpringJmsConsumer {

    private Connection conn;
    private TopicSubscriber topicSubscriber;

    public SpringJmsConsumer(ConnectionFactory connectionFactory, Topic destination ) {
        conn = connectionFactory.createConnection("user", "password");
        Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
        topicSubscriber = session.createDurableSubscriber(destination, "UniqueClientId");
        conn.start();
    }

    public String receiveMessage() throws JMSException {
        TextMessage textMessage = (TextMessage) topicSubscriber.receive();
        return textMessage.getText();
    }
}

并更新 springJmsConsumer

<bean id="springJmsConsumer" class="SpringJmsConsumer">
    <constructor-arg ref="connectionFactory" />
    <constructor-arg ref="messageDestination" />
</bean>

请注意,此代码不管理连接失败。

【讨论】:

  • Hassen,谢谢,但我已经多次尝试您的更新答案,但最终还是只能阅读新消息。我也尝试了您的第一点,但仍然无法正常工作。还有其他方法吗?
  • 当您连接到 AMQ Web 控制台时,您是否在列表中看到了持久订阅者?
  • 我认为您在我更新之前已经尝试过我的答案??因为我现在看到我的代码无法工作,因为 connectionFactory 是在构造函数之后设置的......我现在已经用更好的代码更新它来尝试。请注意,此代码不管理连接失败...您可以测试最新版本吗?我也更新了 springJmsConsumer 定义
  • 谢谢兄弟,这个构造函数的想法很好用。那么我能做些什么来处理连接失败呢?如果连接不为空,我已经放置了 catch 并最终关闭连接,并且还实现了一次性 bean 以在销毁时关闭连接。这很好还是我需要做其他事情来处理连接失败?因为这里我们没有使用 jmsTemplate,而是手动创建连接。
猜你喜欢
  • 1970-01-01
  • 2016-06-17
  • 1970-01-01
  • 2017-11-08
  • 1970-01-01
  • 2010-11-18
  • 2012-05-30
  • 2015-04-06
  • 1970-01-01
相关资源
最近更新 更多