🔰 学习视频 🔰
集数:19—32
🔰 学习格言 🔰
练拳不练功,到老一场空;基础不牢,地动山摇。
文章目录
一、概念
1.1 JavaEE
JavaEE是一套使用Java进行企业级应用开发的大家一致遵循的13个核心规范工业标准。JavaEE平台提供了一一个基于组件的方法来加快设计、开发、装配及部署企业应用程序。
1、JDBC (Java Database) 数据库连接
2、JNDI (Java Naming and Directory Interfaces) Java的命名和目录接口
3、EJB ( Enterprise JavaBean)
4、RMI ( Remote Method Invoke) 远程方法调用
5、Java IDL(Interface Description Language)/CORBA(Common Object Broker Architecture)接口定义语言/公用对象请求代理程序体系结构
6、JSP (Java Server Pages)
7、Servlet
8、XML ( Extensible Markup Language) 可扩展白标记语言
9、JMS (Java Message Service) Java消息服务
10、JTA (Java Transaction API) Java事务API
11、JTS (Java Transaction Service) Java事务服务
12、JavaMail
13、JAF (JavaBean Activation Framework
1.2 JMS
Java消息服务指的是两个应用程序之间进行异步通信的API,它为标准消息协议和消息服务提供了–组通用接口,包括创建、发送、读取消息等,用于支持JAVA应用程序开发。在JavaEE中, 当两个应用程序使用JMS进行通信时,它们之间并不是直接相连的,而是通过一个共同的消息收发服务组件关联起来以达到 解耦 / 异步削峰 的效果。
二、MQ中间件产品
三、JMS的消息
JMS provider:实现JMS接口和规范的消息中间件,也就是MQ服务器。
JMS producer:消息生产者,创建和发送JMS消息的客户端应用。
JMS consumer:消息消费者,接收和处理JMS消息的客户端应用。
JMS message:消息,包含消息头、消息体、消息属性。
3.1 常用消息头
JMSDestination:消息发送的目的地,主要是值Queue和Topic
JMSDeliveryMode:持久和非持久模式
持久模式和非持久模式:
一条持久性的消息:应该被传送“一次仅仅一次”,这就意味者如果JMS提供者出现故障,该消息并不会丢失,它会在服务器恢复之后再次传递。
一条非持久的消息:最多会传送一次,这意味这服务器出现故障,该消息将永远丢失。
JMSExpiration:消息过期时间
可以设置消息在一定时间后过期,默认是永不过期
消息过期时间,等于Destination的send方法中的timeToLive值加上发送时刻的GMT时间值。
如果timeToLive值等于零,则JMSExpiration 被设为零,表示该消息永不过期。
如果发送后,在消息过期时间之后消息还没有被发送到目的地,则该消息被清除。
JMSPriority:优先级
消息优先级,从0-9十个级别,0到4是普通消息,5到9是加急消息。
JMS不要求MQ严格按照这十个优先级发送消息,但必须保证加急消息要先于普通消息到达。
默认是4级。
JMSMessageID:唯一识别每个消息的标识,由MQ产生。
3.2 常用消息体
封装具体的消息数据
5种消息体格式
格式 | 介绍 |
---|---|
TextMessage | 普通字符串消息,包含一个String |
MapMessage | 一个Map类型的消息,key为String类型,值为Java的基本类型 |
BytesMessage | 二进制数组消息,包含一个byte[] |
StreamMessage | Java数据流消息,用标准流操作来顺序的填充和读取 |
ObjectMessage | 对象消息,包含一个可序列化的Java对象 |
以MapMessage为例:
MapMessage mapMessage = session.createMapMessage();
mapMessage.setString("k1","v1");
messageProducer.send(mapMessage);
发送和接收的消息体类型必须一致对应。
3.3 常用消息属性
他们是以属性名和属性值对的形式制定的。可以将属性是为消息头得扩展,属性指定一些消息头没有包括的附加信息,比如可以在属性里指定消息选择器。
消息的属性就像可以分配给一条消息的附加消息头一样。它们允许开发者添加有关消息的不透明附加信息。
它们还用于暴露消息选择器在消息过滤时使用的数据。
发送:
textMessage.setStringProperty("c01", "vip");
messageProducer.send(textMessage);
接收:
System.out.println(textMessage.getStringProperty("c01"));
四、消息可靠性
4.1 持久性
4.1.1 参数设置说明
宕机前,发送三条消息到MQ服务器:
🔶 非持久:当服务器宕机,消息不存在。
messageProducer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
宕机后:
🔶 持久:当服务器宕机,消息依然存在。
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
宕机后:
4.1.2 持久的Queue
队列的的默认传送模式是持久的,此模式保证这些消息只被传送一次 和成功使用一次。对于这些消息,可靠性是优先考虑的因素。
可靠性的另一个重要方面是确保持久性消息传送至目标后,消息服务在向消费者传送它们之前不会丢失这些消息。
4.1.3 持久的Topic
先启动订阅
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumerTopicPersist {
public static final String ACTIVEMQ_URL = "tcp://192.168.150.101:61616";
public static final String TOPIC_NAME = "Topic-Persist";
public static void main(String[] args) throws JMSException, IOException {
System.out.println("customer z3");
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.setClientID("z3");
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(TOPIC_NAME);
TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic, "remark...");
connection.start();
Message message = topicSubscriber.receive();
while (message != null) {
TextMessage textMessage = (TextMessage) message;
System.out.println("------收到的持久Topic:" + textMessage.getText());
message = topicSubscriber.receive(1000L);
}
session.close();
connection.close();
}
}
message = topicSubscriber.receive(1000L); // 等待时间为1s。
message = topicSubscriber.receive(); // 一致阻塞
当接收到消息后,等待1s后得不到消息将自动关闭。
在启动消息的生产者。
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumerTopicPersist {
public static final String ACTIVEMQ_URL = "tcp://192.168.150.101:61616";
public static final String TOPIC_NAME = "Topic-Persist";
public static void main(String[] args) throws JMSException, IOException {
System.out.println("customer z3");
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
Connection connection = activeMQConnectionFactory.createConnection();
connection.setClientID("z3");
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic(TOPIC_NAME);
TopicSubscriber topicSubscriber = session.createDurableSubscriber(topic, "remark...");
connection.start();
Message message = topicSubscriber.receive();
while (message != null) {
TextMessage textMessage = (TextMessage) message;
System.out.println("------收到的持久Topic:" + textMessage.getText());
message = topicSubscriber.receive(1000L);
}
session.close();
connection.close();
}
}
刚刚的订阅者将处理3条消息后关闭。
- 先运行以此消费者,等于向MQ组测,类似于订阅了这个主题。
- 再运行生产者发送信息,此时,无论消费者是否在线,都会接收到,不在线的话,下次连接的时候,会把没收过的消息都接收下来。
4.2 事务
事务偏向于生产者,签收偏向于消费者。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
false
- 只要执行send,就进入到队列中
- 关闭事务,第2个签收参数的设置才有效
true
- 先执行send再执行commit,消息才被真正的提交到队列中。
- 消息需要批量发送,需要缓冲区处理。
作用:批量发送多条消息,其中某一条消息出错时进行回滚。如果全部正确,提交事务。
4.3 签收
事务偏向于生产者,签收偏向于消费者。
🔶 非事务
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
自动签收:Session.AUTO_ACKNOWLEDGE
手动签收:Session.CLIENT_ACKNOWLEDGE
,客户端调用acknowledge()
方法手动签收
允许重复消息:Session.DUPS_OK_ACKNOWLEDGE
🔶 事务
生产事务开启,只要commit后才能将全部消息变为已消费。
在没有事务的情况下,手动签收需要调用acknowledge()
才能防止重复消费。
在开启事务的情况下,手动签收只需要提交commit()
就能消费成功,防止重复消费。如果只调用acknowledge()
,没有提交commit()
,则会出现重复消费。
🔶 签收与事务关系
在事务性会话中,当一个事务被成功提交则消息被自动签收。如果事务回滚,则消息会被再次传送。
非事务会话中,消息何时被确认取决于创建会话时的应答模式(acknowldgement mode)
五、总结
5.1 JMS的点对点
点对点模型是基于队列的,生产者发消息到队列,消费者从队列接收消息,队列的存在使得消息的异步传输成为可能。和我们平时给朋友发送短信类似。
1 如果在Session关闭时有部分消息已被收到但还没有被签(acknowledged), 那当消费者下次连接到相同的队列时,这些消息还会被再次接收。
2 队列可以长久地保存消息直到消费者收到消息。消费者不需要因为担心消息会丢失而时刻和队列保持激活的连接状态,充分体现了异步传输模式的优势。
5.2 JMS发布订阅
JMSPub/Sub模型定义了如何向–个内容节点发布和订阅消息,这些节点被称作topic。
主题可以被认为是消息的传输中介,发布者(publisher)发布消息到主题,订阅者(subscribe)从主题订阅消息。
主题使得消息订阅者和消息发布者保持互相独立,不需要接触即可保证消息的传送。
🔶 非持久订阅
非持久订阅只有当客户端处于激活状态,也就是和MQ保持连接状态才能收到发送到某个主题的消息。
如果消费者处于离线状态,生产者发送的主题消息将会丢失作废,消费者永不会收到。
一句话:先要订阅注册才能接受到发布,只给订阅者发布消息。
🔶 持久订阅
客户端首先向MQ注册一个自己的身份ID识别号,当这个客户端处于离线时,生产者会为这个ID保存所有发送到主题的消息,当客户再次连接到MQ时会根据消费者的ID得到所有当自己处于离线时发送到主题的消息。
非持久订阅状态下,不能恢复或重新派送一个未签收的消息。
持久订阅才能恢复或重新派送一个未签收的消息。