直观的结果:当生产者向 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. 小程序之取标签中内容 例如view,text

    // index.wxml页面 data-url为自定义 {{}}中内容可为后台请求到的数据 也可为固定内容例如:data-text="哈哈哈" data-url="ht ...

  2. shell中的${},##, %% , :- ,:+, ? 的使用

    假设我们定义了一个变量为:file=/dir1/dir2/dir3/my.file.txt 可以用${ }分别替换得到不同的值:${file#*/}:删掉第一个/ 及其左边的字符串:dir1/dir2 ...

  3. Navicat for MySQL用ssh功能连接远程数据库

    转载自:http://holy2010.blog.51cto.com/1086044/518431 实现用本地的ssh隧道起到加密功能 在windows平台上运行Navicat for MySQL(h ...

  4. ASP.NET中Page_Load()与Page_Init()的区别

    Page_Init()事件:aspx初始化时触发,只执行一次,常用于页面初始化,并且执行在page_load之前,如果在aspx的程序中需要使用该方法,那么该方法的类需要继承 System.Web.U ...

  5. Java String常用的两个方法

  6. 虹软2.0 免费人脸识别C#类库分享

    目前只封装了人脸检测部分的类库,供大家交流学习,肯定有问题,希望大家在阅读使用的时候及时反馈,谢谢!使用虹软技术开发完成 戳这里下载SDKgithub:https://github.com/dayAn ...

  7. ubuntu无法关机,卡死

    (1)第一种方法(可行,但开关机出现命令行代码) $ sudo vim /etc/default/grub 将GRUB_CMDLINE_LINUX_DEFAULT="quiet splash ...

  8. 雷林鹏分享:jQuery EasyUI 扩展

    jQuery EasyUI 扩展 Portal(制作图表.列表.球形图等) 数据网格视图(DataGrid View) 可编辑的数据网格(Editable DataGrid) 可编辑的树(Editab ...

  9. 20165327 2017-2018-2《Java程序设计》课程总结

    20165327 2017-2018-2<Java程序设计>课程总结 预备作业一:我期望的师生关系 预备作业二:学习基础和C语言基础调查 预备作业三:预备作业3 Linux安装及学习 第一 ...

  10. LeetCode--342--4的幂

    问题描述: 给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方. 示例 1: 输入: 16 输出: true 示例 2: 输入: 5 输出: false 进阶:你能不使用 ...