【问题标题】:Is it possible to stop and restart a durable subscription using spring jms?是否可以使用 spring jms 停止和重新启动持久订阅?
【发布时间】:2014-01-13 22:17:17
【问题描述】:

我正在使用以下 DefaultMessageListenerContainer 创建一个持久订阅,即使在停机时间也能获取消息,这确实很有效。

@Bean
ConnectionFactory connectionFactory() {
    SingleConnectionFactory connectionFactory = new SingleConnectionFactory(new ActiveMQConnectionFactory(
            AMQ_BROKER_URL));
    connectionFactory.setClientId(CLIENT_ID);
    return connectionFactory;
}

@Bean
DefaultMessageListenerContainer container(final MessageListener messageListener,
        final ConnectionFactory connectionFactory) {
    return new DefaultMessageListenerContainer() {
        {
            setMessageListener(messageListener);
            setConnectionFactory(connectionFactory);
            setDestinationName(JMS_TARGET);
            setPubSubDomain(true);
            setSessionTransacted(true);
            setSubscriptionDurable(true);
            setDurableSubscriptionName(SUBSCRIPTION_ID);                        
            setConcurrentConsumers(1);
        }
    };
}

问题是:当我不再需要订阅时,删除订阅的最佳方法是什么?是否可以暂时删除订阅(并错过一些消息),但稍后再启用它?

到目前为止,唯一可行的方法是关闭 DMLC,然后调用取消订阅。

dmlc.shutdown();
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
session.unsubscribe(SUBSCRIPTION_ID);

这是一个明智的解决方案吗?如何重新开始订阅?

我已经看过this answer,但我真的不知道该怎么做?或者以完全不同的方式订阅和取消订阅会更好吗?

【问题讨论】:

    标签: java spring publish-subscribe spring-jms


    【解决方案1】:

    在我提出我的答案之前,请允许我提一句:暂时删除持久订阅听起来有点像您真的不需要持久订阅。持久订阅适用于需要接收离线时发送的消息的Consumers。如果您仅在某些时候需要消息(例如,当您的消费者连接时),则可以选择非持久主题。

    请注意,这只对Topics 有意义(1:n 通信,发布/订阅)。 Queues 的工作方式略有不同,因为它们用于 1:1 通信(暂时搁置负载平衡等先进技术)。

    持久主题会在消息代理上产生显着的资源开销,并且删除和重新创建订阅可能会导致一遍又一遍地进行昂贵的初始化。

    现在,如果您真的想(暂时)取消订阅持久主题(也适用于“正常”主题),则必须扩展 DefaultMessageListenerContainer

    public class MyContainer extends DefaultMessageListenerContainer {
        public Session retrieveSession() {
            return getSession(); // this is protected, so we wrap it (could also make getSession public)
        }
    }
    

    当您在container 方法中实例化MyContainer(而不是DefaultMessageListenerContainer)时,请确保存储引用:

    protected MyContainer listenerContainer;
    ...
    listenerContainer = new MyContainer(...);
    return listenerContainer;
    

    然后您可以拨打listenerContainer.retrieveSession().unsubscribe(...) 取消订阅。

    还请注意,您只能在该主题的所有会话消费者都关闭并且没有传输中的消息后调用此函数(直接引用documentation:“客户端删除持久订阅是错误的当订阅有一个活动的(未关闭的)消费者时,或者当消费的消息是待处理事务的一部分或尚未在会话中得到确认时。”

    【讨论】:

    • 感谢您的详细解答。我同意您对一次又一次地取消/订阅的担忧,但我很好奇这是如何工作的。它不会是日常操作。因此,问题仍然存在:手动取消/订阅的最佳方式是什么。您的建议“仅”取消订阅,但由于 getSession 需要 JmsResourceHolder 实例,我仍然对此有疑问。此外,虽然我无法测试您的解决方案,但我认为我也会收到错误消息,因为消费者仍处于活动状态。这就是为什么我需要在“我的提案”之前调用 dmlc.shutdown()。
    • 您仍然需要修改您的方法,因为您正在创建一个全新的会话(它将获得自己的客户端 ID),因此您不会从发布订阅的同一会话中取消订阅:您的旧订阅将保持有效
    • 你确定吗? clientId 在 connectionFactory 中设置,因此我认为会话将​​获得相同的 clientId。不过,我怎样才能获得原始会话?如前所述,我需要一个 JmsResourceHolder 实例,对吗?
    • 我不知道您在工厂中设置了 clientID ;) 尽管如此,您始终可以存储对会话的引用或使用上述方法获取它,为此您需要一个 ResourceHandler,但我不我不知道如何获得一个方法。
    • 嗯...好的,那么首先如何获得会话。让我重新表述这个问题:订阅(持久)和取消订阅主题的最佳或最简单的方法是什么?首先在这种情况下使用 DMLC 有什么好处吗?
    猜你喜欢
    • 2020-03-05
    • 2013-12-17
    • 1970-01-01
    • 2017-06-27
    • 2018-01-15
    • 1970-01-01
    • 1970-01-01
    • 2012-07-09
    • 2015-04-04
    相关资源
    最近更新 更多