直观的结果:当生产者向 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. Python 爬起数据时 'gbk' codec can't encode character '\xa0' 的问题

    1.被这个问题折腾了一上午终于解决了,再网上看到有用  string.replace(u'\xa0',u' ') 替换成空格的,方法试了没用. 后来发现 要在open的时候加utf-8才解决问题. 以 ...

  2. 【Mysql】【环境配置】Mac

    参看:http://www.cnblogs.com/chenmo-xpw/p/6102933.html     一.下载dmg包安装 1.下载MySQL dmg 包, 从官网 : http://www ...

  3. _event_phase

    EventId 事件ID Phase 阶段ID,从1开始 StopGUID 击杀生物或摧毁物体当前阶段结束,,正数为生物,负数为物体

  4. js代码点击触发事件

    js触发按钮点击事件 function load(){ //下面两种方法效果是一样的 document.getElementById("target").onclick(); do ...

  5. 后端调用接口在通过webService发布 解决跨域问题

    1.新建一个空的项目 2.添加一个WebService新项   asmx格式的 3.在这里面写方法  加上[WebMethod]标识 前端就可以调用 4.发布WebService  右键服务  添加服 ...

  6. mint18.3 升级linux-libc-dev_4.4.0-102.132 导致外接显示屏无法旋转,设置分辨率

    —————————————————— 补记: 虽然修改之后能旋转,重启还是不行,而且修改显示,经常卡死.还是在第二天早上重装了. 吸取教训,尽量不apt dist-upgrade,不升级内核. 这只是 ...

  7. Lua报错:invalid key to 'next'

    1.问题产生的原因是,在一个循环里对table中的元素先进行置空操作,再进行增加新元素的操作,就会报这个错误. 2.比如下面的例子:(当中间的函数足够复杂并进行封装了的情况下,不会留意到存在这个问题) ...

  8. cocos2dx 编译遇到资源里有.svn文件不能删除报错的问题

    使用cocos compile -p android 对项目进行编译的时候,遇到res文件中包含了只读属性的svn目录,不能进行删除而报错. 错误如下图(build_android.py里面对.svn ...

  9. Codeforces 101572 D - Distinctive Character

    D - Distinctive Character 思路:bfs 使最大的匹配数最小,转换一下,就是使最小的不匹配数最大,用bfs找最大的距离 代码: #pragma GCC optimize(2) ...

  10. LeetCode--414--第三大的数

    问题描述: 给定一个非空数组,返回此数组中第三大的数.如果不存在,则返回数组中最大的数.要求算法时间复杂度必须是O(n). 示例 1: 输入: [3, 2, 1] 输出: 1 解释: 第三大的数是 1 ...