ActiveMQ 笔记(三)JMS规范和落地产品、小知识Broker

个人博客网:https://wushaopei.github.io/    (你想要这里多有)

一、JMS规范概述

1、JavaEE 概述及主要核心规范

JavaEE是一套使用Java进行企业级应用开发的大家一致遵循的13个核心规范工业标准。JavaEE平台提供了一个基于组件的方法来加快设计,开发。装配及部署企业应用程序。

JDBC(Java Databease)数据库连接
JNDI(Java Naming and Directory Interfaces)Java的命令和目录接口
EJB(Enterprise JavaBean)
RMI(Remote Method Invoke)远程方法调用
Java IDL(Interface Description Language)/CORBA(Common Object Broker Architecture)接口定义语言/共用对象请求代理程序体系结构
JSP(Java Server Page)
Servlet
XML(Extensible Markup Language)可标记白标记语言
JMS(Java Message Service)Java消息服务
JTA(Java Transaction API)Java事务API
JTS(Java Transaction Service)Java事务服务
JavaMail
JAF(JavaBean Activation Framework)

2、JMS规范是什么?

JSM,Java Message Service(Java消息服务是JavaEE中的一个技术。

什么是Java消息服务?

Java消息服务指的是两个应用程序之间进行异步通信的API,它为标准协议和消息服务提供了一组通用接口,包括创建、发送、读取消息等,用于支持Java应用程序开发。在JavaEE中,当两个应用程序使用JMS进行通信时,它们之间不是直接相连的,而是通过一个共同的消息收发服务组件关联起来以达到解耦/异步削峰的效果。

3、MQ中间件的其他落地产品

  

消息队列的详细比较:

二、JMS的组成结构和特点

1、JMS的主要构件

JMS 部件 JMS provider

JMS producer

JMS consumer

JMS message

含义 实现JMS 的消息中间件,也就是MQ服务器

消息生产者,创建和发送消息的客户端

消息消费者,接收和处理消息的客户端

JMS 消息,分为消息头、消息属性、消息体

2、5 个主要的消息头:

消息头

JMSDestination

JMSDeliveryMode

JMSExpiration

JMSPriority JMSMessageId

含义

头在哪儿

 

是持久还是非持久

过期时间,默认永久

优先级,默认是4 有0~9 ,5-9 是紧急的,0-4 是普通的

唯一的消息ID

(1)JMSDestination

  • 消息发送的目的地,主要是指Queue和Topic

(2)JMSDeliveryMode

  • 一条持久性的消息:应该被传送“一次仅仅一次”,这就意味着如果JMS提供者出现故障,该消息并不会丢失,它会在服务器恢复之后再次传递。
     
    一条非持久的消息:最多会传递一次,这意味着服务器出现故障,该消息将会永远丢失。

(3)JMSExpiration

  • 消息过期时间,等于Destination的send方法中的timeToLive值加上发送时刻的GMT时间值。
     
    如果timeToLive值等于0,则JMSExpiration被设为0,表示该消息永不过期。
     
    如果发送后,在消息过期时间之后还没有被发送到目的地,则该消息被清除。

(4)JMSPriority

  • JMS不要求MQ严格按照这十个优先级发送消息但必须保证加急消息要先于普通消息到达。默认是4级。

(5)JMSMessageId

  • 唯一标识每个消息的标识由MQ产生。

3、消息属性

(1)定义: 消息属性,封装具体的消息数据

(2)共有5中消息格式:

5 种消息体

TextMessage Mapmessage BytesMessage

StreamMessage

ObjectMessage

含义

普通字符串消息,包含一个String

Map类型的消息,key为Strng类型,而值为Java基本类型

二进制数组消息,包含一个byte[]

Java 数据流消息,用标准流操作来顺序的填充读取

对象消息,包含一个可序列化的Java 对象

(3)发送和接收的消息体类型必须一致对应

消息属性:识别、去重、重点标注

4、消息体

  • 如果需要除消息字段以外的值,那么可以使用消息属性
  • 识别/去重/重点标注等操作非常有用的方法

消息体的示例

message.setStringProperty("username","z3");// 自定义属性

常见消息体接口方法:

      

三、如何保证JMS的可靠性

JMS 可靠性:Persistent 持久性 、 事务 、 Acknowledge 签收

1、持久化

(1)持久化的Queue

// 在队列为目的地的时候持久化消息

messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT)

 

// 队列为目的地的非持久化消息

messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT)

  • 持久化的消息,服务器宕机后消息依旧存在,只是没有入队,当服务器再次启动,消息任就会被消费。
  • 但是非持久化的消息,服务器宕机后消息永远丢失。 而当你没有注明是否是持久化还是非持久化时,Queue 默认是持久化的消息

代码示例 —— 持久化Queue:

MessageProducer messageProducer = session.createProducer(queue);
//设置通过session创建出来的生产者生产的Queue消息为持久性
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);

 进行以上的编码修改后,即使服务器宕机重启后,消费者依旧能获取到宕机前生产的消息,保证了消息的不遗漏。

关于队列的持久化消息

这是队列的默认传递模式,此模式保证这些消息只被传送一次和成功使用一次。对于这些消息,可靠性是优先考虑的因素。

可靠性的另一个重要方面是确保持久性消息传送至目标后,消息服务在向消费者传送它们之前不会丢失这些消息。

(2)持久化的Topic

对于目的地为主题(topic)来说,默认就是非持久化的,让主题的订阅支持化的意义在于:对于订阅了公众号的人来说,当用户手机关机,在开机后任就可以接受到关注公众号之前发送的消息。

注意:先启动定阅消费者再启动定阅生产者

代码实现:持久化topic 的消费者

           ……    // 前面代码相同,不复制了      
        Topic topic = session.createTopic(TOPIC_NAME);
        TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic,"remark...");

         // 5 发布订阅
        connection.start();
        Message message = topicSubscriber.receive();// 一直等
         while (null != message){

             TextMessage textMessage = (TextMessage)message;
             System.out.println(" 收到的持久化 topic :"+textMessage.getText());
             message = topicSubscriber.receive(3000L);    // 等1秒后meesage 为空,跳出循环,控制台关闭

         }
   ……

持久化生产者:

          ……     

        MessageProducer messageProducer = session.createProducer(topic);

        // 6 通过messageProducer 生产 3 条 消息发送到消息队列中
        // 设置持久化topic 在启动
        messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT); 
        connection.start();

        for (int i = 1; i < 4 ; i++) {
            // 7  创建字消息
            TextMessage textMessage = session.createTextMessage("topic_name--" + i);

            // 8  通过messageProducer发布消息
            messageProducer.send(textMessage);

            MapMessage mapMessage = session.createMapMessage();
            //    mapMessage.setString("k1","v1");
            //     messageProducer.send(mapMessage);

        }

        // 9 关闭资源

      …… 

当生产者启动后:

消息被消费,并且: (因为我在receive方法中设置了如果接收到消息后3秒还没有消息就离线,也也可以设置永久存活)

类似场景:微信公众号订阅发布

2、Transaction:事务

createSession的第一个参数为true 为开启事务,开启事务之后必须在将消息提交,才可以在队列中看到消息

Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

提交:

session.commit();

事务开启的意义在于,如果对于多条必须同批次传输的消息,可以使用事务,如果一条传输失败,可以将事务回滚,再次传输,保证数据的完整性。

对于消息消费者来说,开启事务的话,可以避免消息被多次消费,以及后台和服务器数据的不一致性。举个栗子:

如果消息消费的 createSession 设置为 ture ,但是没有 commit ,此时就会造成非常严重的后果,那就是在后台看来消息已经被消费,但是对于服务器来说并没有接收到消息被消费,此时就有可能被多次消费。


注意:

  • 关闭事务,那第2个签收参数的设置需要有效
  • 开启事务,当消息需要需要批量提交,需要缓冲处理
  • 事务偏生产者/签收偏消费者

3、Acknowledge:签收 (简称 ack)

createSession的第二个参数为true 为签收。

签收的两种方式:

(1)非事务

    Session.AUTO_ACKNOWLEDGE    自动签收,默认

 

    Session.Client_ACKNOWLEDGE     手动签收

手动签收需要 acknowledge

     textMessage.acknowledge( );

而对于开启事务时,设置手动签收和自动签收没有多大的意义,都默认自动签收,也就是说事务的优先级更高一些。

注意:非事务性签收,允许重复消息时,使用以下签收方式:

Session.DUPS_OK_ACKNOWLEDGE

(2)事务:

 

而对于开启事务时,设置手动签收和自动签收没有多大的意义,都默认自动签收,也就是说事务的优先级更高一些。

Session session = connection.createSession(true,Session.AUTO_ACKNOWLEDGE);
//Session session = connection.createSession(true,Session.CLIENT_ACKNOWLEDGE);   //  也是自动签收   

        ……

session.commit();  

注意:但是开启事务没有commit 任就会重复消费;生产事务开启,只有commit后才能将全部消息变为已消费

(3)签收和事务的关系

  • 在事务性会话中,当一个事务被成功提交则消息被自动签收。如果事务回滚,则消息会被再次传送
  • 非事务性会话中,消息何时被确认取决于创建会话时的应答模式( acknowledgement mode)
小结: 事务级别高于签收,开启事务时,默认自动签收。但同时又需要对消费的消息进行提交,以避免重复消费

4、JMS的小总结

(1)JMS的点对点总结

点对点模型是基于队列的,生产者发送消息到队列,消费者从队列接收消息,队列的存在使得消息的异步传输成为可能。
和我们平时给朋友发送短信类似。 
  • 如果在Session关闭时有部分消息被收到但还没有被签收(acknowledge),那当消费者下次连接到相同的队列时,这些消息还会被再次接收
  • 队列可以长久的保存消息直到消费者收到消息。消费者不需要因为担心消息会丢失而时刻和队列保持激活的链接状态,充分体现了异步传输模式的优势

 (2)JMS的发布订阅总结

JMS Pub/Sub 模型定义了如何向一个内容节点发布和订阅消息,这些节点被称作topic
  • 主题可以被认为是消息的传输中介,发布者(publisher)发布消息到主题,订阅者(subscribe)从主题订阅消息。
  • 主题使得消息订阅者和消息发布者保持互相独立不需要解除即可保证消息的传送

 非持久订阅:

非持久订阅只有当客户端处于激活状态,也就是和MQ保持连接状态才能收发到某个主题的消息。
如果消费者处于离线状态,生产者发送的主题消息将会丢失作废,消费者永远不会收到。

一句话:先订阅注册才能接受到发布,只给订阅者发布消息。

持久订阅:

客户端首先向MQ注册一个自己的身份ID识别号,当这个客户端处于离线时,生产者会为这个ID保存所有发送到主题的消息,当客户再次连接
到MQ的时候,会根据消费者的ID得到所有当自己处于离线时发送到主题的消息
 
当持久订阅状态下,不能恢复或重新派送一个未签收的消息。 
持久订阅才能恢复或重新派送一个未签收的消息。

场景应用:

当所有的消息必须被接收,则用持久订阅。当消息丢失能够被容忍,则用非持久订阅

四、Broker

1、Broker是什么?

Broker的定义,broker 就是实现了用代码形式启动 ActiveMQ 将 MQ 内嵌到 Java 代码中,可以随时启动,节省资源,提高了可靠性。相当于一个ActiveMQ服务器实例。

目的:在用的时候再去启动这样能节省了资源,也保证了可用性。

即,就是将 MQ 服务器作为了 Java 对象

2、Broker配置(嵌入式)

使用多个配置文件启动 activemq

cp activemq.xml  activemq02.xml 

// 以active02 启动mq 服务器
./activemq start xbean:file:/myactivemq/apache-activemq-5.15.9/conf/activemq02.xml 

把小型 activemq 服务器嵌入到 java 代码: 不在使用linux 的服务器

需要的包:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.5</version>
</dependency>

代码实现:

public class Embebroker {

    public static void main(String[] args) throws Exception {

        // broker 服务
        BrokerService brokerService = new BrokerService();

        // 把小型 activemq 服务器嵌入到 java 代码
        brokerService.setUseJmx(true);

        // 原本的是 192.……  是linux 上的服务器,而这里是本地windows 的小型mq 服务器
        brokerService.addConnector("tcp://localhost:61616");

        brokerService.start();

    }
}

说明:以上代码启动后和Linux上的ActiveMQ是一样的,Broker相当于一个Mini版本的ActiveMQ

posted @ 2020-02-09 20:52  维宇空灵  阅读(306)  评论(0编辑  收藏  举报