JMS(Java Message Service)

五迷三道 提交于 2019-12-18 11:51:48

是什么

JMS是Java EE中的一部分,好比是盘子和点心的关系,JMS是Java EE中的额一个组成部分。
在这里插入图片描述
在这里插入图片描述

JMS的组成

JMS有四部分组成。分别为:

JMS privider: 实现JMS接口与规范的消息中间件,也就是我们的MQ服务器。
JMS producer:消息生产者,创建与发送JMS消息的客户端应用。
JMS consumer:消息的消费者,接受与处理JMS消息的客户端应用。
JMS message:JMS的消息。

JMS message的组成

JMS message由三部分组成:消息头、消息头和消息属性。

消息头

在JMS message中包含很多消息头,如下所示:
在这里插入图片描述
几个比较重要的消息头如下:

1、JMSdestination:message发送的目的地,比如queue和topic。
2、JMSDeliveryMode:主要是指定消息的持久或者非持久化。
在这里插入图片描述
在这里插入图片描述
3、JMSExpiration:消息存活时间。
在这里插入图片描述
4、JMSPriority:消息优先级。
在这里插入图片描述

5、JMSMessageID:唯一识别每个消息的标志,由MQ产生。

消息体

消息体顾名思义作用就是封装具体消息的数据
那么消息的数据到底有多少种格式呢,有五种。其中最常用的为TextMessageMapMessage两种。
1、TextMessage:普通的字符串消息,包含一个string。
2、MapMessage:一个map类型的消息,key为stirng类型,而value 为java的基本类型。如下:
在这里插入图片描述
如果MapMessage中的value放的是char类型数据,则创键方式如下所示:
在这里插入图片描述
3、BytesMessage:二进制数组消息,包含一个byte[]。
4、StreamMessage:java数据流消息,用标准流操作来顺序的填充或读取。
5、ObjectMessage:对象消息,包含一个可序列化的java对象。

注意:
发送和接受的消息体类型必须是一致对应的。如发送的是TextMessage,则接收方式如下:

 consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if (null != message && message instanceof TextMessage) {
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("msg-----" + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

消息属性

如果需要使用消息头以外的值,那么可以使用消息属性(一种加强型的api),可用于识别/去重/重点标注等操作。
在这里插入图片描述

JMS的可靠性

persistent:持久性

基于队列queue

1、非持久化:
在这里插入图片描述
2、持久化
在这里插入图片描述
3、默认情况下ActiveMQ是持久化的,即向MQ中发送消息之后即使MQ服务器宕机挂掉然后重新启动之后,消费者也可以从MQ中取到原来发送的消息;而如果使用上述方法设置消息非持久化,那么在重新启动之后就不能取到原来的数据,原来的数据就会被丢失。

基于主题topic发布订阅

持久的发布主题生产者:

package com.atguigu.durable;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
/**
 * ActiveMQ
 * JMS(Java Message Service)的生产者编码
 * @author xxy
 * @date 2019/12/16
 */
public class DurableJmsProduceTopic {
    private static final String ACTIVE_URL = "tcp://192.168.0.119:61616";
    private static final String TOPIC_NAME = "topic-01";

    public static void main(String[] args) throws JMSException {
        //创建连接工厂,按照指定的url地址,采用默认用户名和密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVE_URL);
        //通过连接工厂创建连接
        Connection connection = activeMQConnectionFactory.createConnection();

        //创建回话session,两个参数:事务和签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        //常见目的地(队列或queue者主题topic)
        Topic topic = session.createTopic(TOPIC_NAME);
        //创建生产者
        MessageProducer producer = session.createProducer(topic);
        //设置持久化的topic生产者
        producer.setDeliveryMode(DeliveryMode.PERSISTENT);
        connection.start();
        //使用MessageProducer生产3条消息发送到MQ的队列里
        for (int i = 0; i < 3; i++) {
            //创建消息
            TextMessage textMessage = session.createTextMessage("msg------" + i);
            //通过生产者MessageProducer发送给MQ
            producer.send(textMessage);
        }
        //关闭资源
        producer.close();
        session.close();
        connection.close();
    }
}

持久的发布主题消费者:

package com.atguigu.durable;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
/**
 * ActiveMQ
 * JMS(Java Message Service)的生产者编码
 * @author xxy
 * @date 2019/12/16
 */
public class DurableJmsConsumerTopic {
    private static final String ACTIVEMQ_URL = "tcp://192.168.0.119:61616";
    private static final String TOPIC_NAME = "topic-01";

    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("订阅者z3");
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        Connection connection = factory.createConnection();
        connection.setClientID("z3");
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Topic topic = session.createTopic(TOPIC_NAME);
        TopicSubscriber durableSubscriber = session.createDurableSubscriber(topic, "remark..");

        connection.start();

        Message message = durableSubscriber.receive();
        while (null != message) {
            TextMessage textMessage = (TextMessage) message;
            System.out.println(textMessage.getText());
            message = durableSubscriber.receive();
        }

        session.close();
        connection.close();
    }
}

持久化的表现:
持久性的订阅者首先启动进行订阅,就算中途离线关掉了系统,也会在下次连接上线后收到消息。

transaction:事务

无论是在生产者还是消费者的时候都要创建一个session会话,如下

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

其中第一个参数就是事务,false代表关闭事务,true代表使用事务。

如果生产者和消费者都设置为false,那么在producer.send(message)之后,消息就会直接传到队列中,consumer.receive之后消息就会直接取出来;
如果生产者和消费者都设置为true,那么必须手动session.commit()进行提交,出错之候可以session.rollback()。如果不commit,那么:producer.send()之后,消息不会发送到消息队列中,并且因为没有commit,consumer.receive()会多次消费同一条消息。

acknowledge 签收模式

签收模式的效果如何与事务的开启与否有着密切的关系,主要常用的签收模式包括两种:自动签收AUTO_ACKNOWLEDGE和手动签收CLIENT_ACKNOWLEDGE。

1、在事务为false的时候,签收模式为:AUTO_ACKNOWLEDGE
在此模式下,为自动签收。不用任何操作就会签收,不会出现二次消费的情况
2、在事务为false的时候,签收模式为:CLIENT_ACKNOWLEDGE
在此模式下消息不会自动签收,必须使用message.acknowledge()手动对消息进行签收,否则会出现二次消费。
3、在事务为true的时候,签收模式为:AUTO_ACKNOWLEDGE
在此模式下,并且调用session.commit()进行事务提交的情况下为自动签收。若事务不提交,则会出现二次消费的情况。
4、在事务为true的时候,签收模式为:CLIENT_ACKNOWLEDGE
在此模式下,并且调用session.commit()进行事务提交的情况下为手动签收。若事务不提交,则会出现二次消费的情况。即使message.acknowledge()进行消息手动签收也会出现二次消费。

总结:
在事务关闭的情况下,消息是否自动签收取决于签收模式,如AUTO_ACKNOWLEDGE或者CLIENT_ACKNOWLEDGE;在事务开启的情况下,并且调用commit方法,则无论签收模式是什么都会自动提交,在事务开启的情况下,若不调用commit方法,则无论签收模式是什么都不会自动提交。

总结

JMS点对点模式总结

在这里插入图片描述

JMS发布订阅模式总结

在这里插入图片描述

非持久订阅

在这里插入图片描述

持久订阅

在这里插入图片描述

用哪个

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!