一. 消息中间件
什么是消息中间件
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。对于消息中间件,常见的角色大致也就有Producer(生产者)、Consumer(消费者)
常见的消息中间件产品
优势分析
场景应用
二. JMS
什么是 JMS
JMS(Java Messaging Service)是 Java 平台上有关面向消息中间件的技术规范,它便于消息系统中的 Java 应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。
JMS 本身只定义了一系列的接口规范,是一种与厂商无关的 API,用来访问消息收发系统。消息格式
JMS 定义了五种不同的消息正文格式 :
· TextMessage–一个字符串对象
· MapMessage–一套名称-值对
· ObjectMessage–一个序列化的 Java 对象
· BytesMessage–一个字节的数据流
· StreamMessage – Java 原始值的数据流JMS 消息传递类型
a. 点对点
b. 发布/ 订阅模式
三. ActiveMQ
四. JMS入门案例
- 准备工作
- 1 引入坐标
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.13.4</version>
</dependency>
点对点模式
- 1 创建 QueueProducer
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.junit.Test;
import javax.jms.*;
public class QueueProducer {
/**
* 需求:发送消息
* 模式:点对点模式
*/
@Test
public void sendMessage() throws Exception{
//指定消息服务器发送地址
String brokerURL = "tcp://192.168.66.66:61616";
//创建消息连接工厂对象
ConnectionFactory cf = new ActiveMQConnectionFactory(brokerURL);
//从工厂中获取连接对象
Connection connection = cf.createConnection();
//开启连接
connection.start();
//从连接中获取消息回话对象session
//参数1:
// true: 表示使用的是消息事务确认提交模式 Session.SESSION_TRANSACTED
// false: 消息自动确认模式
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//创建消息存储空间,且给这个空间起一个名称
Queue queue = session.createQueue("myQueue");
//创建消息发送者,且告知消息往哪儿发送
MessageProducer producer = session.createProducer(queue);
//创建消息对象
TextMessage message = new ActiveMQTextMessage();
message.setText("齐天大圣孙悟空,修炼了八九玄功?");
//发送消息
producer.send(message);
producer.close();
session.close();
connection.close();
}
}
- 2 创建 QueueConsumer
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.aspectj.weaver.ast.Var;
import org.junit.Test;
import javax.jms.*;
import java.util.Map;
public class QueueConsumer {
/**
* 需求:接受消息
* 模式:点对点模式
*/
@Test
public void receiveMessage() throws Exception{
//指定消息服务器发送地址
String brokerURL = "tcp://192.168.66.66:61616";
//创建消息连接工厂对象
ConnectionFactory cf = new ActiveMQConnectionFactory(brokerURL);
//从工厂中获取连接对象
Connection connection = cf.createConnection();
//开启连接
connection.start();
//从连接中获取消息回话对象session
//参数1:是否使用消息事务模式 参数2: 消息确认模式
// true: 使用消息事务, 事务提交并确认模式 Session.SESSION_TRANSACTED
// false: 不使用消息事务, 自动确认模式 Session.AUTO_ACKNOWLEDGE
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//指定消息存储空间,且给这个空间起一个名称
Queue queue = session.createQueue("myQueue");
//指定消息接受者,且指定从哪儿接受消息
MessageConsumer consumer = session.createConsumer(queue);
//监听接受
consumer.setMessageListener(new MessageListener() {
@Override
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
TextMessage m = (TextMessage) message;
//获取消息
String text = m.getText();
System.out.println("接受消息:"+text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
//等待输入,让端口阻塞,服务一直开启
System.in.read();
consumer.close();
session.close();
connection.close();
}
}
上述代码中创建 session 的两个参数:
第 1 个参数 是否使用事务
第 2 个参数 消息的确认模式
AUTO_ACKNOWLEDGE 自动确认
CLIENT_ACKNOWLEDGE 客户端手动确认
DUPS_OK_ACKNOWLEDGE 自动批量确认
SESSION_TRANSACTED 事务提交并确认
发布/订阅模式
- 1 创建 TopicProducer
//创建消息存储空间,且给这个空间起一个名称
//点对点:queue
//发布订阅:topic
//发布订阅和点对点根本区别在于发生消息数据结构不一样。
Topic topic = session.createTopic("myTopic");
//创建消息发送者,且告知消息往哪儿发送
MessageProducer producer = session.createProducer(topic);
和 [ 点对点模式 ] 的 Producer 代码相识度99% ,唯有上述两行不同
- 2 创建 TopicConsumer
//从连接中获取消息回话对象session
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//指定消息存储空间,且给这个空间起一个名称
Topic topic = session.createTopic("myTopic");
//指定消息接受者,且指定从哪儿接受消息
MessageConsumer consumer = session.createConsumer(topic);
和 [ 点对点模式 ] 的 Consumer 代码相识度99% ,唯有上述几行不同
五. Spring 整合 JMS , ActiveMQ
准备
spring架构中已经整合了JMS, 故只需将ActiveMQ 交由spring-JMS 接管即可
– 引入 activemq-client 坐标
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.13.4</version>
</dependency>
生产者
- 1 配置文件 applicationContext-mq-producer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--把activeMQ交给spring管理-->
<bean id="targetConnnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg name="brokerURL" value="tcp://192.168.66.66:61616"></constructor-arg>
</bean>
<!--创建spring jms组件提供工厂对象,无缝接管activeMQ-->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnnectionFactory"></property>
</bean>
<!--spring jms提供消息发送模板对象-->
<bean class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
<!--点对点消息空间-->
<bean class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="oneQueue"></constructor-arg>
</bean>
<!--发布订阅消息空间-->
<bean class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="oneTopic"></constructor-arg>
</bean>
</beans>
- 2 生产者测试类 SpringJmsProducer
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mq-producer.xml")
public class SpringJmsProducer {
//注入消息发送模板对象
@Autowired
private JmsTemplate jmsTemplate;
//注入消息目的地:点对点
@Autowired
private ActiveMQQueue activeMQQueue;
//注入消息目的地:发布订阅
@Autowired
private ActiveMQTopic activeMQTopic;
/**
* 需求:点对点发送消息
*/
@Test
public void sendMessageByP2P(){
//使用模板发现消息
jmsTemplate.send(activeMQQueue, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("只要988!");
}
});
}
/**
* 需求:发布订阅发送消息
*/
@Test
public void sendMessageByPS(){
//使用模板发现消息
jmsTemplate.send(activeMQTopic, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("只要988!hhhhhhh");
}
});
}
}
消费者
- 1 配置文件 applicationContext-mq-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--把activeMQ添加到spring容器-->
<bean id="targetConnnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg name="brokerURL" value="tcp://192.168.66.66:61616"></constructor-arg>
</bean>
<!--创建spring jms组件提供工厂对象,无缝接管activeMQ-->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnnectionFactory"></property>
</bean>
<!--点对点消息空间-->
<bean id="oneQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="oneQueue"></constructor-arg>
</bean>
<!--发布订阅消息空间-->
<bean id="topic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="oneTopic"></constructor-arg>
</bean>
<!--自定义消息监听器01 处理点对点消息-->
<bean id="queListener" class="com.pyg.search.listener.QueListener"></bean>
<!--spring jms提供消息监听容器:自动触发监听器,自动实现消息接收-->
<bean id="messageListenerContainer01" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!--指定接受消息服务器-->
<property name="connectionFactory" ref="connectionFactory"></property>
<!--指定从消息服务器那个空间接受-->
<property name="destination" ref="oneQueue"></property>
<!--指定接受消息监听器-->
<property name="messageListener" ref="queListener"></property>
</bean>
<!--自定义消息监听器02 处理订阅消息-->
<bean id="topListener " class="com.pyg.search.listener.TopListener "></bean>
<!--spring jms提供消息监听容器:自动触发监听器,自动实现消息接收-->
<bean id="messageListenerContainer02" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<!--指定接受消息服务器-->
<property name="connectionFactory" ref="connectionFactory"></property>
<!--指定从消息服务器那个空间接受-->
<property name="destination" ref="topic"></property>
<!--指定接受消息监听器-->
<property name="messageListener" ref="topListener "></property>
</bean>
</beans>
- 2 创建自定义监听类 QueListener, 处理点对点消息
import com.alibaba.fastjson.JSON;
import com.pyg.pojo.TbItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import java.util.List;
import java.util.Map;
public class QueListener implements MessageListener{
@Override
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
TextMessage m = (TextMessage) message;
//获取消息
String text = m.getText();
System.out.println("接受消息:"+text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
- 3 创建自定义监听类 TopListener , 处理订阅消息
import com.alibaba.fastjson.JSON;
import com.pyg.pojo.TbItem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import java.util.List;
import java.util.Map;
public class TopListener implements MessageListener{
@Override
public void onMessage(Message message) {
if(message instanceof TextMessage){
try {
TextMessage m = (TextMessage) message;
//获取消息
String text = m.getText();
System.out.println("接受消息:"+text);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
无论是处理点对点, 还是订阅, 只要自定义一个监听类
implements MessageListener
, 重写onMessage(Message message)
方法就可以了, 服务器启动, 加载配置文件, 只要监听到消息, 自定义监听类会自动处理消息