目标1:理解消息中间件、JMS等概念
目标2:掌握JMS点对点与发布订阅模式的收发消息
目标3:掌握SpringJms ActiveMQ
JMS入门
一、消息中间件
什么是消息中间件?
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行*分布式系统*的集成。
通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
对于消息中间件,常见的角色大致也就有*Producer(生产者)、Consumer(消费者)*
Controller和Service解耦通过JMS建立连接
常见的消息中间件产品:
- ActiveMQ
- RabbitMQ
- ZeroMQ
- Kafka
二、JMS简介
什么是JMS?
JMS(Java Messaging Service)是Java平台上有关面向消息中间件的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。
JMS本身只定义了一系列的接口规范,是一种与厂商无关的 API,用来访问消息收发系统。
JMS 定义了五种不同的消息正文格式,以及调用的消息类型
- TextMessage–一个字符串对象
- MapMessage–一系列key-value对
- ObjectMessage–一个序列化的Java对象
- BytesMessage–一个字节的数据流
- StreamMessage–Java原始值的数据流
JMS消息传递类型
对于消息的传递有两种类型(两种模型):
1. **点对点**:即一个生产者和一个消费者一一对应;
PTP(Point-to-point)无需订阅直接获取消息并消费掉,之后的consumer无法获取消息
即一个消息传递 多个消费者随机一个消费者处理一个消息
graph LR
p1[Producer]-->Queue;
p2[Producer]-->Queue;
Consumer--监听Listener-->Queue;
Queue--获取消息-->Consumer;
2. 另一种是**发布/订阅模式**:即一个生产者产生消息进行发送后,可以由多个消费者进行接收
Publish/subscribe(pub/sub)需要先订阅才能获取发布的消息
即一个消息发布 多个订阅的消费者全部接收到这个消息,每个消费者都处理这个消息
graph LR
p1[Producer]-->Topic;
p2[Producer]-->Topic;
Topic--监听获取消息-->c1[Consumer];
Topic--监听获取消息-->c2[Consumer];
三、ActiveMQ
ActiveMQ简介
ActiveMQ可以持久化,消息存储在本地的data文件夹,也可以设置存储在数据库
ActiveMQ管理软件端口号8161
TCP端口号61616,远程连接ActiveMQ服务器走端口号61616
建议一个功能一个消息queue/topics
生产者producer:
graph LR
ActiveMQConnectionFactory-->Connection;
Connection-->Session;
Session-->Queue/Topics;
Queue/Topics-->producer;
Session-->producer;
Session-->Message;
Message-->producer;
producer--send-->发送消息待发;
s[Session]--commit-->手动提交消息;
消费者consumer
graph LR
ActiveMQConnectionFactory-->Connection;
Connection-->Session;
Session-->Queue/Topics;
Queue/Topics-->consumer;
Session-->consumer;
consumer--listener监听-->Message;
Message-->consumer;
api:
ActiveMQConnectionFactory
Connection createConnection();
Connection
void start();
Session createSession(Boolean boolean, int acknowledgeMode);
第1个参数 是否使用事务
第2个参数 消息的确认模式
• AUTO_ACKNOWLEDGE = 1 自动确认
• CLIENT_ACKNOWLEDGE = 2 客户端手动确认
• DUPS_OK_ACKNOWLEDGE = 3 自动批量确认
• SESSION_TRANSACTED = 0 事务提交并确认
Session
1.创建传递消息类型 分为点对点队列
Queue createQueue(String name);//队列名称
Topics createTopics(String name);//发布/订阅名称,生产者和消费者通过消息传递类型的名称绑定消息
2.创建消息
createTextMessage();
void setText(string)//设置字符串
String getText();//获取字符串
createTextMessage(String text);//字符串 构造传参
createObjectMessage();
void setObject(Serializable object);//设置对象
Object getObject();//获取对象
createObjectMessage(Serializable object);//对象需要实现序列化接口 构造传参
createBytesMessage();//字节
void writerByte(Byte byte);//写 字节
Byte readByte();//读 字节
createMapMessage();//map集合
void setObject(String name,Object value);//设置键值对
Object getObject(String name);//获取指定键的值
createStreamMessage();//数据流
void writer*(* t);//写 字节
* read*();//读 字节
Queue extends Destination
String getQueueName();//获得队列名
Topics extends Destination
String getTopicName();//获得主题名
MessageProducer
void send(Message message);//发送消息 默认使用创建时指定的传递消息
void send(Destination destination, Message message);//指定传递消息(队列,发布/订阅) 传递Message消息
MessageConsumer
void setMessageListener(MessageListener listener)
//MessageListener接口
void onMessage(Message message)//抽象方法
Message receive();//只接收一次消息
ActiveMQ的Linux的安装
- 解压缩tar zxvf apache-activemq-5.12.0-bin.tar.gz
- 启动服务./activemq start
- 访问192.168.25.135:8161查看后台界面,用户名和密码都是admin
```
后台界面介绍
Queues:队列消息,也叫点对点消息
Number of Pending Message:没有消费的消息数量
Number of Consumers:连接得消费者
Messages Enqueued:进入到队列中得消息数量
Messages Dequeued:已经出列得消息数量
Topics:订阅消息, 类似广播的消息
```
JMS使用流程
pom坐标依赖
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.13.4</version>
</dependency>
</dependencies>
一、点对点(队列)模式
发送&接收消息的9个步骤
//1.创建连接工厂
//2.获取连接
//3.启动连接
//4.获取session
//5.创建队列对象 //**点对点和发布订阅此处创建的对象为Queue或Topic**
//6.创建消息生产者对象或消费者
//7.创建消息 //此处consumer接受消息setMessageListener
//8.使用生成者发送消息 //等待键盘输入System.in.read();
//9.关闭资源
点对点生产者Producer示例:
package cn.itcast.test;
public class QueueProducer {
public static void main(String[] args) throws JMSException {
//1.创建连接工厂 若连接远程服务器需要写远程地址加tcp端口号,本地无需写参数
ActiveMQConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.25.130:61616");
//2.获取连接
Connection connection = connectionFactory.createConnection();
//3.启动连接
connection.start();
//4.获取session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5.创建队列对象
Queue queue = session.createQueue("test-queue");
//6.创建消息生产者对象
MessageProducer producer = session.createProducer(queue);
//7.创建消息
TextMessage textMessage = session.createTextMessage("欢迎来到神奇的品优购世界!");
//8.使用生成者发送消息
producer.send(textMessage);
//9.关闭资源
producer.close();
session.close();
connection.close();
}
}
上述代码中第4步创建session 的两个参数:
第1个参数 是否使用事务
第2个参数 消息的确认模式
• AUTO_ACKNOWLEDGE = 1 自动确认
• CLIENT_ACKNOWLEDGE = 2 客户端手动确认
• DUPS_OK_ACKNOWLEDGE = 3 自动批量确认
• SESSION_TRANSACTED = 0 事务提交并确认
运行后通过ActiveMQ管理界面查询
点对点消费者Consumer示例:
package cn.itcast.test;
public class QueueConsumer {
public static void main(String[] args) throws JMSException, IOException {
// 1.创建连接工厂
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.130:61616");
// 2.获取连接
Connection connection = connectionFactory.createConnection();
// 3.启动连接
connection.start();
// 4.获取session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5.创建队列对象
Queue queue = session.createQueue("test-queue");
// 6.创建消息消费者
MessageConsumer consumer = session.createConsumer(queue);
// 7.接受消息
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
// 获取文本消息对象
TextMessage textMessage = (TextMessage) message;
try {
String text = textMessage.getText();// 提取文本
System.out.println(text);
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// 8.等待键盘输入
System.in.read();
// 9.关闭资源
consumer.close();
session.close();
connection.close();
}
}
二、发布/订阅模式
发送&接收消息的9个步骤
//1.创建连接工厂
//2.获取连接
//3.启动连接
//4.获取session
//5.创建一个订阅 Topic
//6.创建消息生产者对象或**消费者**
//7.创建消息 //**接受消息setMessageListener**
//8.使用生成者发送消息 //**等待键盘输入System.in.read();**
//9.关闭资源
创建订阅生产者TopicProducer
package cn.itcast.test;
public class TopicProducer {
public static void main(String[] args) throws JMSException {
//1.创建连接工厂
ActiveMQConnectionFactory connectionFactory=new ActiveMQConnectionFactory("tcp://192.168.25.130:61616");
//2.创建连接
Connection connection = connectionFactory.createConnection();
//3.启动连接
connection.start();
//4.创建会话
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//5.创建一个订阅
Topic topic = session.createTopic("test-topic");
//6.创建消息生产者
MessageProducer producer = session.createProducer(topic);
//7.创建消息
TextMessage textMessage = session.createTextMessage("您的手机该续费了!");
//8.发送消息
producer.send(textMessage);
//9.关闭资源
producer.close();
session.close();
connection.close();
}
}
创建订阅消费者TopicConsumer
package cn.itcast.test;
public class TopicConsumer {
public static void main(String[] args) throws JMSException, IOException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory("tcp://192.168.25.131:61616");
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("test-topic");
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message arg0) {
// TODO Auto-generated method stub
TextMessage message = (TextMessage) arg0;
try {
System.out.println("收到消息"+message.getText());
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
System.in.read();
consumer.close();
session.close();
connection.close();
}
}
Spring整合JMS
SpringJms整合JMS消息中间件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
消息中间件使用ActiveMQ
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.11.2</version>
</dependency>
JmsTemplate:
send(Destination dest,MessageCreator creator)
//MessageCreator接口
//抽象方法
Message createMessage(Session session);
convertAndSend(Destination dest,Object obj)
Spring整合生产者配置:
通过JmsTemplate操作ActiveMQ
<context:component-scan base-package="cn.itcast.demo"></context:component-scan>
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.135:61616"/>
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<!--这个是队列目的地,点对点的 文本信息 可以写在代码 写在配置文件方便管理
ActiveMQQueue实现了Destination
-->
<bean id="queueTextDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="queue_text"/>
</bean>
<!--这个是订阅模式 文本信息-->
<bean id="topicTextDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="topic_text"/>
</bean>
Spring整合消费者配置:
consumer监听类需要实现MessageListener接口 实现其抽象方法onMessage(Message message);//message为要监听获取的消息
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.135:61616"/>
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory"/>
</bean>
<!--这个是队列目的地,点对点的 文本信息 可以写在代码 写在配置文件方便管理
ActiveMQQueue实现了Destination
-->
<bean id="queueTextDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="queue_text"/>
</bean>
<!--这个是订阅模式 文本信息-->
<bean id="topicTextDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="topic_text"/>
</bean>
<!-- 以下不同于producer 且没有template配置 -->
<!-- 我的监听类 配置consumemr实现MessageListener的监听类 -->
<bean id="myMessageListener" class="cn.itcast.demo.MyMessageListener"></bean>
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!-- 第一个连接,第二个监听目标,第三个consumer监听类配置到监听容器中 -->
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="topicTextDestination" />
<property name="messageListener" ref="myMessageListener" />
</bean>
发布/订阅模式与队列模式
web层发送消息
service层监听消息
producer发送消息:常用于Controller的处理器
@Autowired
private JmsTemplate jmsTemplate;
@Autowired//发布订阅
@Qualifier("queueSolrCreateDestination")
private Destination queueSolrCreateDestination;
@Autowired//点对点队列
@Qualifier("topicPageCreateDestination")
private Destination topicPageCreateDestination;
//发送solr库消息,对solr库进行数据新增
//通过JmsTemplate完成producer发送消息步骤
//调用方法send(Destination desc,MessageCreator) 第一个参数是采用哪个消息目标,第二个参数创建消息对象Message的一个接口
jmsTemplate.send(queueSolrCreateDestination,new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
//此被实现的抽象方法参数Session 可以创建Message类型
return session.createObjectMessage(ids);//传递对象ObjectMessage
}
});
//改为发送生成页面消息
for (final Long id : ids) {
jmsTemplate.send(topicPageCreateDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
// TODO Auto-generated method stub
return session.createTextMessage(id.toString());
}
});
*OR*
jmsTemplate.send(topicPageCreateDestination,x->x.createTextMessage(id.toString()));
consumer监听:常用于Service层 单独写一个类实现MessageListener
//队列的监听器
@Component
public class ItemSearchCreateListener implements MessageListener{//监听器需要实现MessageListener接口
@Autowired
private ItemSearchService itemSearchService;//监听器监听到id执行对应功能所需要调用的service层对象
@Override
public void onMessage(Message message) {//onMessage(Message message) 监听方法 实现的抽象方法,参数为监听获取的消息对象Message
try {
ObjectMessage objectMessage = (ObjectMessage)message;//监听到的消息为对象ObjectMessage 注意发送什么类型的Message就接收什么类型的Message
Long[] ids = (Long[]) objectMessage.getObject();
itemSearchService.importItems(ids);
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//发布订阅的监听器
@Component
public class ItemPageCreateListener implements MessageListener{
@Autowired
private ItemPageService itemPageService;
@Override
public void onMessage(Message message) {
TextMessage ms = (TextMessage) message;
try {
String goodsId = ms.getText();
itemPageService.createItemHtml(Long.parseLong(goodsId));
System.out.println("创建页面消息=====");
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
其他
tcc分段式补偿
message的大小有限制 5000字符
message只发id由service和controller根据id查库 防止message太大出错
上(发)下(收)游