为什么要用 MQ 消息中间件
关注 园码生活详细了解
接下来举个例子,我们以前上学时候有不会的问题都会去问老师,然后假如这时候有一百个学生问问题,那我们就得排队等着,那一方面会浪费时间,另外一方面老师也不一定能处理完问题,那这时候老师看到这种情况就说:同学们你们把不会的问题总结出来由班长统一接收,然后再由班长交给我,这样一来我们自己是不是不会浪费时间,另外一方面老师也可以有自己的时间处理自己的事情
MQ 消息中间件能干嘛?
削峰,异步,解耦
通过消息排队和消息传递模型在分布式下提供应用解耦,弹性伸缩,流量削峰,异步通信,数据同步等功能。
发送者把消息发送给服务器,消息服务将消息存放在如干个队列和主题中,在合适的时候,消息服务器会将消息转发给接受者。在这个过程中发送和接收是异步的,无需等待,而且发送者和接收者的生命周期也没有必然关系。
ActiveMQ消息中间件的简单使用
添加相应的依赖架包
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.11.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.xbean</groupId>
<artifactId>xbean-spring</artifactId>
<version>3.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
Spring整合ActiveMQ
JMS的编码架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXanoxBc-1606130000099)(https://imgkr2.cn-bj.ufileos.com/52e4bc2a-31aa-46ac-b67e-fee138bbea9a.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=cDDUgI4It2F8eERD9X2Wy%252B9E3hg%253D&Expires=1606201024)]
两大模型的特性
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jURDkTGz-1606130000102)(https://imgkr2.cn-bj.ufileos.com/e9f24022-045d-4c59-9024-477011ce11b7.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=OoMZFm5AnLR3s2jBqICJkLzw%252FNo%253D&Expires=1606201455)]
消息生产者
package com.yang.activemq.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProduce {
public static final String ACTIVEMQ_URL="tcp://127.0.0.1:61616";
public static final String QUEUE_NAME="quenue01";
public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory connectionFactory=new ActiveMQConnectionFactory();
// 连接工厂
Connection connection=connectionFactory.createConnection();
connection.start();
// 创建回话
//第一个事务,第二个签收
Session session=connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Destination destination =session.createQueue(QUEUE_NAME);
// 创建目的地
Queue queue= session.createQueue(QUEUE_NAME);
// 创建消费者
MessageProducer producer=session.createProducer(queue);
for (int i=1;i<=3;i++){
// 创建消息发送到队列里面
TextMessage textMessage=session.createTextMessage("msg---->"+i);
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("消息发送完成");
}
}
消息消费者
package com.yang.activemq.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer {
public static final String ACTIVEMQ_URL="tcp://127.0.0.1:61616";
public static final String QUEUE_NAME="quenue01";
public static void main(String[] args) throws JMSException, IOException {
//创建连接工厂
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
// 连接工厂
Connection connection = connectionFactory.createConnection();
connection.start();
// 创建回话
//第一个事务,第二个签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Destination destination =session.createQueue(QUEUE_NAME);
// 创建目的地
Queue queue = session.createQueue(QUEUE_NAME);
// 创建消费者
MessageConsumer consumer=session.createConsumer(queue);
// 同步阻塞
// while (true){
// TextMessage message= (TextMessage) consumer.receive();
// if (message!=null){
// System.out.println("接收到的消息是"+message.getText());
// }else {
// break;
// }
// }
// consumer.close();
// session.close();
// connection.close();
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if (null!=message&&message instanceof TextMessage){
TextMessage textMessage= (TextMessage) message;
try {
System.out.println("消息内容---->"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
System.in.read();
consumer.close();
session.close();
connection.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GjIMqvmm-1606130000105)(https://imgkr2.cn-bj.ufileos.com/2fe7c800-a41e-49f0-a325-6e9f257413af.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=NVNSP%252FHzTlRt9ONeBRw%252FVUXKe9A%253D&Expires=1606203724)]
TOPIC
主题topic特点
生产者将消息发布到topic中每个消息有多个消费者,生产者和消费者之间有时间上的相关性。
生产者生产时,topic不保存消息,它是勿状态的不落地,假如没有人订阅就是废消息
package com.yang.activemq.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
public class JmsProduce {
public static final String ACTIVEMQ_URL="tcp://127.0.0.1:61616";
public static final String TOPIC_NAME="topic01";
public static void main(String[] args) throws JMSException {
//创建连接工厂
ActiveMQConnectionFactory connectionFactory=new ActiveMQConnectionFactory();
// 连接工厂
Connection connection=connectionFactory.createConnection();
connection.start();
// 创建回话
//第一个事务,第二个签收
Session session=connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Destination destination =session.createQueue(QUEUE_NAME);
// 创建目的地
Topic topic= (Topic) session.createTopic(TOPIC_NAME);
// 创建消费者
MessageProducer producer=session.createProducer(topic);
for (int i=1;i<=3;i++){
// 创建消息发送到队列里面
TextMessage textMessage=session.createTextMessage("msg---->"+i);
producer.send(textMessage);
}
producer.close();
session.close();
connection.close();
System.out.println("消息发送完成");
}
}
消费者
package com.yang.activemq.queue;
import org.apache.activemq.ActiveMQConnectionFactory;
import javax.jms.*;
import java.io.IOException;
public class JmsConsumer {
public static final String ACTIVEMQ_URL="tcp://127.0.0.1:61616";
public static final String TOPIC_NAME="topic01";
public static void main(String[] args) throws JMSException, IOException {
//创建连接工厂
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
// 连接工厂
Connection connection = connectionFactory.createConnection();
connection.start();
// 创建回话
//第一个事务,第二个签收
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Destination destination =session.createQueue(QUEUE_NAME);
// 创建目的地
Topic queue = session.createTopic(TOPIC_NAME);
// 创建消费者
MessageConsumer consumer=session.createConsumer(queue);
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
if (null!=message&&message instanceof TextMessage){
TextMessage textMessage= (TextMessage) message;
try {
System.out.println("消息内容---->"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
});
System.in.read();
consumer.close();
session.close();
connection.close();
}
}
TOPIC和QUEUE的区别
比较项目 | TOPIC模式队列 | QUEUE模式队列 |
---|---|---|
工作模式 | 订阅-发布模式如果当前没有订阅者会丢弃,如果有订阅者,那么这些订阅者会受到消息 | 负载均衡模式如果当前没有消费者模式,消息不会丢弃,如果有多个消费者,那么一条消息也会发送给其中一个消费者,并且要求消费者ACK消息 |
有无状态 | 无状态 | QUEUE数据默认会在mq服务器上以文件形式保存 |
传递完整性 | 如果没有订阅者消息会丢弃 | 消息不会丢弃 |
处理效率 | 由于消息要按照订阅者的数量进行复制,所以处理性能会随着订阅者的增加而明显 | 由于一条消息只发送给一个消费者,所以就算消费者再多,性能也不会明显降低 |
Spring整合ActtiveMQ
applicationContext-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:amp="http://activemq.apache.org/schema/core"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd">
<!--1.连接工厂-->
<amp:connectionFactory
id="connectionFactory"
brokerURL="tcp://127.0.0.1:61616"
userName="admin"
password="admin"
/>
<!--2.缓存连接工厂-->
<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="connectionFactory"/>
<property name="sessionCacheSize" value="5"/>
</bean>
<!--3.配置消息监听组件扫描-->
<context:component-scan base-package="com.yang.springcloud"/>
<!--4.配置监听器(点对点)-->
<!--
destination-type: 目标的类型(queue:点对点,topic:发布订阅)
-->
<jms:listener-container connection-factory="cachingConnectionFactory" destination-type="queue">
<jms:listener destination="spring_queue" ref="queueListener"/>
</jms:listener-container>
<!--5.配置监听器(发布订阅)-->
<jms:listener-container connection-factory="cachingConnectionFactory" destination-type="topic">
<jms:listener destination="spring_topic" ref="topicListener"/>
</jms:listener-container>
</beans>
applicationContext-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:amp="http://activemq.apache.org/schema/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
<!--1.创建连接工厂对象-->
<amp:connectionFactory
id="connetionFactory"
brokerURL="tcp://127.0.0.1:61616"
userName="admin"
password="admin"
/>
<!--2.创建缓存连接工厂-->
<bean id="cachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<!--注入连接工厂-->
<property name="targetConnectionFactory" ref="connetionFactory"/>
<!--缓存消息数据-->
<property name="sessionCacheSize" value="5"/>
</bean>
<!--3.创建用于点对点发送的JmsTemplate-->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<!--注入缓存连接工厂-->
<property name="connectionFactory" ref="cachingConnectionFactory"/>
<!--指定是否为发布订阅模式-->
<property name="pubSubDomain" value="false"/>
</bean>
<!--4.创建用于发布订阅发送的JmsTemplate-->
<bean id="jmsTopicTemplate" class="org.springframework.jms.core.JmsTemplate">
<!--注入缓存连接工厂-->
<property name="connectionFactory" ref="cachingConnectionFactory"/>
<!--指定是否为发布订阅模式-->
<property name="pubSubDomain" value="true"/>
</bean>
</beans>
消息队列模型的代码:
package com.yang.springcloud;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
* 点对点
*/
@Component // 放入SpringIIOC容器,名称queueListener
public class QueueListener implements MessageListener {
//用于接收消息
@Override
public void onMessage(Message message) {
if(message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("queue接口消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
订阅-发布模型的代码:
package com.yang.springcloud;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
/**
* 发布订阅
*/
@Component // 放入SpringIIOC容器,名称queueListener
public class TopicListener implements MessageListener {
@Override
public void onMessage(Message message) {
if(message instanceof TextMessage){
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("topic接口消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
演示Spring整合ActiveMQ具体代码:
package com.yang.springcloud.producter;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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;
import javax.jms.TextMessage;
/**
* 演示Spring与ActiveMQ整合
*/
@RunWith(SpringJUnit4ClassRunner.class) // junit与spring整合
@ContextConfiguration("classpath:applicationContext-producer.xml") // 加载spring配置文件
public class SpringProducer {
//点对点模式
@Autowired
@Qualifier("jmsQueueTemplate")
private JmsTemplate jmsQueueTemplate;
//发布订阅模式
@Autowired
@Qualifier("jmsTopicTemplate")
private JmsTemplate jmsTopicTemplate;
/**
* 点对点发送
*/
@Test
public void ptpSender(){
/**
* 参数一:指定队列的名称
* 参数二:MessageCreator接口,我们需要提供该接口的匿名内部实现
*/
jmsQueueTemplate.send("spring_queue", new MessageCreator() {
//我们只需要返回发送的消息内容即可
@Override
public Message createMessage(Session session) throws JMSException {
//创建文本消息
TextMessage textMessage = session.createTextMessage("spring test message");
return textMessage;
}
});
System.out.println("消息发送已完成");
}
/**
* 发布订阅发送
*/
@Test
public void psSender(){
jmsTopicTemplate.send("spring_topic", new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
//创建文本消息
TextMessage textMessage = session.createTextMessage("spring test message--topic");
return textMessage;
}
});
System.out.println("消息发送已完成");
}
}
用于消费方启动监听类
package com.yang.springcloud.consumer;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.io.IOException;
/**
* 用于启动消费方监听
*/
public class SpringConsuer {
public static void main(String[] args) throws IOException {
//1.加载spring配置
ClassPathXmlApplicationContext
cxt = new ClassPathXmlApplicationContext("classpath:applicationContext-consumer.xml");
//2.启动
cxt.start();
//3.阻塞方法,让程序一直处于等待状态
System.in.read();
}
}
SpringBoot整合ActiveMQ
导入所需依赖
<!--导入所需依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--springboot与ActiveMQ的整合依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
SpringBoot整合ActiveMQ生产者
代码示例:
application.yml
配置文件
server:
port: 9001 #端口
spring:
application:
name: activemq-producer # 服务名称
# springboot与activemq整合配置
activemq:
broker-url: tcp://127.0.0.1:61616 # 连接地址
user: admin # activemq用户名
password: admin # activemq密码
# 指定发送模式 (点对点 false , topic发布订阅 true)
jms:
pub-sub-domain: true
# 自己定义目标名称(队列或主题)
activemq:
name: springboot_topic
生产者的代码示例:
package com.yang.springcloud;
import com.yang.producter.ProducerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = ProducerApplication.class)
public class SpringBootProducer {
//JmsMessagingTemplate: 用于工具类发送消息
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Value("${activemq.name}")
private String name;
@Test
public void ptpSender(){
/**
* 参数一:队列的名称或主题名称
* 参数二:消息内容
*/
jmsMessagingTemplate.convertAndSend(name,"spring boot message--topic,我收到了");
System.out.println("完成了");
}
}
启动类
package com.yang.producter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/*** 生产者启动类 */
@SpringBootApplication
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication.class,args);
}
}
SpringBoot整合ActiveMQ消费者
具体代码示例:
application.yml配置文件
server:
port: 9002 #端口
spring:
application:
name: activemq-consumer # 服务名称
# springboot与activemq整合配置
activemq:
broker-url: tcp://127.0.0.1:61616 # 连接地址
user: admin # activemq用户名
password: admin # activemq密码
# 指定发送模式 (点对点 false , 发布订阅 true)
jms:
pub-sub-domain: true
activemq:
name: springboot_topic
消费者类
package com.yang.consumer.listener;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.TextMessage;
/**
* 用于监听消息类(既可以用于队列的监听,也可以用于主题监听)
*/
@Component // 放入IOC容器
public class MsgListener {
/**
* 用于接收消息的方法
* destination: 队列的名称或主题的名称
*/
@JmsListener(destination = "${activemq.name}")
public void receiveMessage(Message message){
if(message instanceof TextMessage){
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("接收消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
消费者启动类
package com.yang.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 消息消费者启动类
*/
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}