直观的结果:当生产者向 topic 发送消息,

1. 若不存在持久订阅者和在线的普通订阅者,这个消息不会保存,当普通订阅者上线后,它是收不到消息的。

2. 若存在离线的持久订阅者,broker 会为该持久订阅者保存消息,当该持久订阅者上线后,会收到消息。

本质:producer 发送消息给 topic,broker 接收消息并且分发给 consumers。consumers 包括持久订阅者和在线的普通订阅者,对于持久订阅者,broker 把消息添加到它的 message cursor 中;对于普通订阅者,broker 直接分发消息。

如果,1个 topic 有2个持久订阅者,并且这2个持久订阅者都不在线,这时 producer 向 topic 发送1条消息,则 broker 会保存2条消息。因此,如果1个 topic 有很多不在线的持久订阅者,会导致 broker 消耗过多存储。

对于持久化的消息,这很好验证:为方便查看消息,将 broker 持久化方式配置为jdbc,则可以在 ACTIVEMQ_MSGS 表中看到持久化消息。

持久订阅者1:

new ActiveMQConnectionFactory("tcp://localhost:61616?jms.clientID=10086");
...
TopicSubscriber consumer = session.createDurableSubscriber(destination, "subscriber_zhang");

持久订阅者2:

new ActiveMQConnectionFactory("tcp://localhost:61616?jms.clientID=10087");
...
TopicSubscriber consumer = session.createDurableSubscriber(destination, "subscriber_zhang");

对于持久消息,验证 broker 为每个持久订阅者保存1条消息:
1. 启动 broker;
2. 分别启动2个持久订阅者,然后关闭它们,这样 broker 有了2个离线的持久订阅者;
3. 启动 producer 向 topic 发送1条消息;
4. 查看 ACTIVEMQ_MSGS 表

对于非持久消息,直接跟代码了,这里不说明。

下图是 Topic 接收消息并分发的调用栈:

// org.apache.activemq.broker.region.policy.SimpleDispatchPolicy
// 分发策略很简单,就是遍历consumers
public boolean dispatch(MessageReference node, MessageEvaluationContext msgContext, List<Subscription> consumers)
throws Exception {
int count = 0;
for (Subscription sub : consumers) {
// Don't deliver to browsers
if (sub.getConsumerInfo().isBrowser()) {
continue;
}
// Only dispatch to interested subscriptions
if (!sub.matches(node, msgContext)) {
sub.unmatched(node);
continue;
} //持久化订阅是 DurableTopicSubscription
//普通订阅是 TopicSubscription
sub.add(node);
count++;
} return count > 0;
}

持久化订阅:

// org.apache.activemq.broker.region.DurableTopicSubscription
public void add(MessageReference node) throws Exception {
if (!active.get() && !keepDurableSubsActive) {
return;
}
// 调用 PrefetchSubscription.add
super.add(node);
} // org.apache.activemq.broker.region.PrefetchSubscription
public void add(MessageReference node) throws Exception {
synchronized (pendingLock) {
// The destination may have just been removed...
if( !destinations.contains(node.getRegionDestination()) && node!=QueueMessageReference.NULL_MESSAGE) {
// perhaps we should inform the caller that we are no longer valid to dispatch to?
return;
} // Don't increment for the pullTimeout control message.
if (!node.equals(QueueMessageReference.NULL_MESSAGE)) {
enqueueCounter++;
}
//首先加入到 message cursor 中,pending 类型为 StoreDurableSubscriberCursor
pending.addMessageLast(node);
}
dispatchPending();
}

持久化订阅者上线后,也会触发消息分发动作即 dispatchPending,调用栈如下图:

持久化订阅者使用的 message cursor 是 StoreDurableSubscriberCursor。

普通订阅:

org.apache.activemq.broker.region.TopicSubscription.add(MessageReference node)

ActiveMQ topic 普通订阅和持久订阅的更多相关文章

  1. ActiveMQ 持久订阅者,执行结果与初衷相违背,验证离线订阅者无效,问题解决

    导读 最新在接触ActiveMQ,里面有个持久订阅者模块,功能是怎么样也演示不出来效果.配置参数比较简单(配置没几个参数),消费者第一次运行时,需要指定ClientID(此时Broker已经记录离线订 ...

  2. JMS学习(六)--提高非持久订阅者的可靠性 以及 订阅恢复策略

    一,非持久订阅者 和 实时消费消息 在这篇文章中区分了Domain为Pub/Sub.Destination为Topic时,消费者有两种:持久订阅者 和 非持久订阅者. 对于持久订阅者而言,只要订阅了某 ...

  3. JMS学习七(ActiveMQ之Topic的持久订阅)

    非持久化订阅持续到它们订阅对象的生命周期.这意味着,客户端只能在订阅者活动时看到相关主题发布的消息.如果订阅者不活动,它会错过相关主题的消息.如果花费较大的开销,订阅者可以被定义为durable(持久 ...

  4. ActiveMQ queue和topic,持久订阅和非持久订阅

    消息的 destination 分为 queue 和 topic,而消费者称为 subscriber(订阅者).queue 中的消息只会发送给一个订阅者,而 topic 的消息,会发送给每一个订阅者. ...

  5. JMS学习(五)--ActiveMQ中的消息的持久化和非持久化 以及 持久订阅者 和 非持久订阅者之间的区别与联系

    一,消息的持久化和非持久化 ①DeliveryMode 这是传输模式.ActiveMQ支持两种传输模式:持久传输和非持久传输(persistent and non-persistent deliver ...

  6. activemq的高级特性:消息持久订阅

    activemq的高级特性之消息持久订阅 如果采用topic模式发送的时候,mq关闭了或消费者关闭了.在启动的时候,就会收不到mq发送的消息,所以就会出现消息持久订阅. 消息持久订阅:第一:消息要持久 ...

  7. ActiveMQ 事务、集群、持久订阅者、ActiveMQ监控

    JMS介绍 JMS是什么? JMS的全称Java Message Service,既Java消息服务. JMS是SUN提供的旨在统一各种MOM(Message-Oriented Middleware) ...

  8. activemq订阅发布模式(非持久订阅)

    生产者JMSProducer: package com.sun.test.aircraft.activemq.topic; import org.apache.activemq.ActiveMQCon ...

  9. ActiveMQ 复杂类型的发布与订阅

    很久没po文章了,但是看到.Net里关于ActiveMQ发送复杂类型的文章确实太少了,所以贴出来和大家分享 发布: //消息发布 public class Publisher { private IC ...

随机推荐

  1. [luogu]P1852跳跳棋

    题目重点是每次不能跳过两个棋子 即对于每一个棋子的状态(a,b,c) (a<b<c) 最多有两种移动的方式 1.中间往两边跳 (a,b,c)-->(2b-a,a,c)或(a,c,2b ...

  2. React Native组件之BackAndroid !安卓手机的物理返回键的使用

    ok!在安卓手机上,当我们用物理返回键的时候,会以一次性的将程序退出来,这样是很不好的体验,所以就需要使用RN的物理返回键组件:BackAndroid,其原理也就是 分析路由,然后pop()这样! o ...

  3. Codeforces 786 B. Legacy

    题目链接:http://codeforces.com/contest/786/problem/B 典型线段树优化连边,线段树上的每一个点表示这个区间的所有点,然后边数就被优化为了至多${nlogn}$ ...

  4. go 单进程并发

    demo1 // This sample program demonstrates how to create goroutines and // how the scheduler behaves. ...

  5. loglog()函数

    数据: xd = [1, 2, 3] yd = [0.6, 0.2, 0.2] matlab中双对数函数: 命令: loglog(xd, yd, 'blacko-', 'MarkerFaceColor ...

  6. Microsoft Active Directory(LDAP)连接常见错误代码

    接下来显示的认证错误类似于这样: "The exception is [ LDAP: error code 49 - 80090308: LdapErr: DSID-0Cxxxxxx, co ...

  7. 越来越“简单”的Java

    Java,20岁了.从我写下第一行Java代码,迄今已有十余年了,眼见Java——这个当年刚刚找到自己成长方向的懵懂少年,成长为如今当之无愧的业界王者.它已拥有世界上最庞大的开发者社区,以及无可匹敌的 ...

  8. django使用表单

    假设你想从表单接收用户名数据,一般情况下,你需要在HTML中手动编写一个如下的表单元素: <form action="/your-name/" method="po ...

  9. input 输入值的监听 禁止输入特殊字符

    1.input  输入值的监听 //用于监听input的值变化(input的值产生变化才会触发事件) (function ($) { $.fn.watch = function (callback) ...

  10. newborn, infant, toddler以及baby的区别

    1.An infant (from the Latin word infans, meaning "unable to speak" or "speechless&quo ...