AMQ的持久化问题

前言

​ 前面一篇AMQ专题中,我们发现对于Topic这种类型的消息,即使将deliveryMode设置为持久化,只要生产者在消费者之前启动。消息生产者发布的消息还是会丢失。这是符合JMS规范的。

当然,作为一个如此活跃的开源消息中间件,在实现JMS基本规范之后,必然会通过扩展的方式来实现Topic的持久化订阅。

而所谓的deliveryMode持久化和订阅持久化还是两个不同的概念。本篇博客我们就通过实例来一探究竟。

DeliveryMode持久化

​ 在前面一篇中,我们通过producer.setDeliveryMode(DeliveryMode.PERSISTENT);将消息传递特性置为持久化,但是当消息类型是topic的时候,不管该值设置为啥,只要先启动Producer,那么对于后启动的Consumer都无法获取原来发布的主题。

​ 那么这个DeliveryMode究竟是用来干啥的呢?

  • DeliveryMode中的是否持久化,指的是当重启activeMQ之后,原来队列或者主题中未被消费的消息是否仍然保留

​ 我这里自己通过代码进行了如下测试,测试步骤和结果如下:

  1. 创建producer,并将producer的deliveryMode设置成持久化,运行producer
  2. 在消息被consumer消费之前,重启activeMQ
  3. 运行consumer,发现接收到了activeMQ重启之前Producer发送的消息
  4. 修改producer,将producer的deliveryMode设置成非持久化,运行producer
  5. 在消息被consumer消费之前,重启activeMQ
  6. 运行consumer,没有接收到任何消息,原producer产生的消息丢失

​ 持久化和非持久化最终队列控制台分别如下:

     至此,不难发现,deliveryMode的是否持久化是针对activeMQ服务器是否重启而言的。对于不支持持久化的设置,当mq重启之后,没有被消费的消息就会丢失。而支持持久化的设置,只要消息没有被消费,重启mq,仍然能被新加入的consumer消费。

订阅持久化

​ JMS的规范是没有要求实现订阅持久化的。所幸的是activeMQ实现了这个特性。个人认为所谓的订阅持久化相对于消息的持久化,不过是一种伪持久化。先不做太多说明,我们直接看一个示例代码:

生产者

public class SimpleProducer {
public static void main(String[] args) {
// STEP1: 得到连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL); Connection connection = null;
Session session = null;
MessageProducer topicProducer = null;
Destination topicDestination = null;
try {
// STEP2: 从连接工厂得到连接并且启动连接
connection = connectionFactory.createConnection();
connection.start(); // STEP3: 获取会话
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // STEP4: 创建主题
topicDestination = session.createTopic("KiDe-topic-Demo"); // STEP5: 创建消息生产者
topicProducer = session.createProducer(topicDestination);
topicProducer.setDeliveryMode(DeliveryMode.PERSISTENT); // 设置为持久化 // STEP6: 发送消息
for (int i=0; i<20; i++) {
TextMessage message = session.createTextMessage("Producer message:" + i);
topicProducer.send(message);
} // STEP7: 如果开启了事务 ,此时需要调用session提交操作
// session.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
}
}
}
}
}

消费者

public class SimpleConsumer {
public static void main(String[] args) {
// STEP1: 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
ActiveMQConnection.DEFAULT_PASSWORD, ActiveMQConnection.DEFAULT_BROKER_URL); Connection connection = null;
Session session = null;
MessageConsumer topicConsumer = null;
try {
// STEP2: 从连接工厂得到连接并且启动连接
connection = connectionFactory.createConnection();
connection.setClientID("1"); // 如果要进行持久化订阅,必须对连接设置clientID
connection.start(); // STEP3: 获取会话
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); // STEP4: 创建持久化订阅者
TopicSubscriber topicSubscriber = session.createDurableSubscriber(session.createTopic("KiDe-topic-Demo"), "1"); // STEP5: 设置消息接收监听
topicSubscriber.setMessageListener(new MessageListener() { @Override
public void onMessage(Message paramMessage) {
TextMessage message = (TextMessage) paramMessage;
try {
System.out.println("消费者接收到主题消息:" + message.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
TimeUnit.SECONDS.sleep(200); // 睡眠200秒,使得客户端可以接收到对应消息
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
}
}
}
}
}

​ 最终我的验证步骤和结果如下:

  1. 运行producer,向activeMQ发送主题消息
  2. 运行consumer,发现未收到任何消息
  3. 运行producer,此时运行中的consumer接收到了topic消息
  4. 停止运行consumer,重新运行producer
  5. 重新运行consumer,此时consumer接收到了刚刚producer产生的消息
  6. 创建consumer的session的时候,同时创建两个同clientId的session时会报同一通道已被占用的错误

​ 分析以上步骤,我最终对这种伪持久化订阅的总结如下:

  • 要实现伪持久化订阅,必须先向activeMQ发布持久化订阅消息,通过clientId来标识不同的订阅渠道。

    如果在发布持久化订阅消息之前producer就向mq发送了topic消息,那么consumer还是没法接收

  • activeMQ确定是否是同一持久化订阅者的依据条件有两个:connection.setClientID("3")中的clientId

    以及session.createDurableSubscriber(session.createTopic("KiDe-topic-Demo"), "12")中的name

总结

  • deliveryMode的持久化和订阅持久化是两个不同的概念,二者互不干扰,组合实现业务需求
  • 需要弄清参数的实际意义第一步自己动手写实例,看运行结果是否与自己预期一致。第二步则是情况允许的时候,多看源码,掌握好的代码和设计

ActiveMQ专题2: 持久化的更多相关文章

  1. ActiveMQ的消息持久化机制

    为了避免意外宕机以后丢失信息,需要做到重启后可以恢复消息队列,消息系统一般都会采用持久化机制. ActiveMQ的消息持久化机制有JDBC,AMQ,KahaDB和LevelDB,无论使用哪种持久化方式 ...

  2. 分布式-信息方式-ActiveMQ的消息存储持久化

    ActiveMQ的消息存储持久化■概述ActiveMQ不仅支持 persistent和 non-persistent两种方式,还支持消息的恢复( recovery)方式PTPQueue的存储是很简单的 ...

  3. 学习ActiveMQ(八):activemq消息的持久化

    1. 持久化方式介绍前面我们也简单提到了activemq提供的插件式的消息存储,在这里再提一下,主要有以下几种方式: AMQ消息存储-基于文件的存储方式,是activemq开始的版本默认的消息存储方式 ...

  4. ActiveMQ专题1: 入门实例

    序 好久没有写博客了,最近真的是可以说是忙成狗了.项目的事和自己的终身大事忙得焦头烂额,好在是一切都是越来越好了...... 趁着项目今天唯一的一点喘息时间,加上项目开始接触到的mq,开始写一篇amq ...

  5. ActiveMQ消息的持久化策略

    持久化消息和非持久化消息的存储原理: 正常情况下,非持久化消息是存储在内存中的,持久化消息是存储在文件中的.能够存储的最大消息数据在${ActiveMQ_HOME}/conf/activemq.xml ...

  6. ActiveMQ订阅模式持久化实现

    实现步骤:1.配置发送xml,applicationContext-send.xml <?xml version="1.0" encoding="UTF-8&quo ...

  7. ActiveMQ安装与持久化消息

    activityMQ官网:http://activemq.apache.org/ 有windows版与linux版  windows版启动 在bin目录下双击activemq.bat linux版的安 ...

  8. ActiveMQ学习笔记(8)----ActiveMQ的消息存储持久化

    1. 概述 ActiveMQ不仅支持persistent和non-persistent两种方式,还支持消息的恢复(recovery)方式. 2. PTP Queue的存储是很简单的,其实就是FIFO的 ...

  9. JMS学习九(ActiveMQ的消息持久化到Mysql数据库)

    1.将连接Mysql数据库的jar文件,放到ActiveMQ的lib目录下 2.修改ActiveMQ的conf目录下的active.xml文件,修改数据持久化的方式 2.1  修改原来的kshadb的 ...

随机推荐

  1. 区间DP石子合并问题 & 四边形不等式优化

    入门区间DP,第一个问题就是线性的规模小的石子合并问题 dp数组的含义是第i堆到第j堆进行合并的最优值 就是说dp[i][j]可以由dp[i][k]和dp[k+1][j]转移过来 状态转移方程 dp[ ...

  2. 如何永久关闭选项"Tools > Close Other Forms"

    http://www.cnblogs.com/quanweiru/archive/2013/03/30/2990945.html Oracle EBS里Tools>Close Other For ...

  3. 分形之闵可夫斯基(Minkowski)

    与上一篇文章分形之正方形折线相似,闵可夫斯基分形也是分形出正方体,不同之处是它分出了两个正方体. 核心代码: static void FractalMinkowski(const Vector3&am ...

  4. Kali Linux渗透测试实战 1.1 Kali Linux简介

    1.1 Kali Linux简介 如果您之前使用过或者了解BackTrack系列Linux的话,那么我只需要简单的说,Kali是BackTrack的升级换代产品,从Kali开始,BackTrack将成 ...

  5. strom ui Topology 可视化视图各个指标含义说明

    In the visualization, spout components are represented as blue, while bolts are colored between gree ...

  6. ASP.NET Core ef启用数据迁移

    创建一个项目 通过Nuget获取EF Core相关的扩展包 appsettings.json 建立数据库连接串 创建数据库上下文EntityDbContext类,用于实体类映射数据库表 使用包管理器控 ...

  7. Spring 扫描标签<context:component-scan/>

    一. <context:annotation-config/> 此标签支持一些注入属性的注解, 列如:@Autowired, @Resource注解 二. <context:comp ...

  8. 全世界最顶级黑客同时沸腾在DEF CON 25,是怎样一种体验?

    2017,我在这里!圆你黑客梦!!   被称为黑客“世界杯”与“奥斯卡”的美国黑帽技术大会Black Hat和世界黑客大会DEF CON 是众多黑客心中最神圣的梦!   有位小表弟告诉我说:Black ...

  9. Centos7调整swap分区

    如何让服务器响应更快?如何避免应用出现内存不足的错误?最简单的方法就是增加交换空间.Swap是存储盘上的一块自留地,操作系统可以在这里暂存一些内存里放不下的东西. 这从某种程度上相当于增加了服务器的可 ...

  10. WebDriver高级应用实例(9)

    9.1封装操作表格的公用类 目的:能够使自己编写操作表格的公用类,并基于公用类进行表格中的元素的各类操作 被测网页的网址的HTML代码: <html> <body> <t ...