1.写在前面
本人是一枚编程小白,该文章内容是经过大量的资料翻阅,以本人的理解进行一个讲解。我会尽可能的说的很明白,如果您认为我理解的有问题,欢迎评论区留言,万分感谢。
2.什么是消息队列?
消息队列也就是我们说的MQ,他是一个可独立部署的中间件产品。消息,顾名思义就是发送出来的信息,队列,大家学过一点数据结构应该就会很清楚队列是什么,在这里给大家打一个比方。一根管子你从管子的一头放乒乓球,先放进去的一定是先从另一头出来的,所以队列有个特点是“先进先出”。简单来说:系统A发送消息给MQ,系统B再从MQ中读取消息。
3.消息队列有什么作用?
解耦,异步,削峰
这三个作用大家如果想要详细了解,可以直接去查一下,网上资源很多。为了方便大家之后的理解我在这里简单说明一下。
解耦:
比如:现在系统A产生了一条数据需要推送给系统B,我可以直接远程调用系统B的接口,将这条数据推送给系统B。但是现在系统C也需要这条数据,然后我再远程调用系统C的接口,将这条数据推送给他。现在系统D也有这个需求了...大家有没有发现这样是不是很麻烦,因为以后可能还有很多系统需要这条数据,你就得不断地去更新升级代码。
现在,有了MQ我们可以怎么做,系统A产生数据后发送给MQ,刚才也说了MQ是一个中间件,系统B,系统C可以直接从MQ中取这条数据,包括以后有新的系统需要这条数据,系统A也不需要做更新升级。
异步:
比如:系统A在推送这条数据的时候,必须先推给系统B,在推给系统C,在推给系统D,这样增加了耗时,增加了时间复杂度。可能大家会想到用线程去推送,因为线程可以并发的去推送,可以实现推送给B,C,D同步执行,但是这只是3系统,不保证后面会有很多系统有这种需求,因此这种方式会占用很多线程,增加了空间复杂度。(我不确定时间复杂度与空间复杂度的概念我应用的是否准确,欢迎大家留言。)
现在,系统A把消息交给MQ之后他就可以继续往下执行了,其它系统去获取消息只需从MQ中拿就行了。在这边接着给大家讲一个概念,生产者和消费者。生产者就是产生消息的那一方,就是目前的系统A,消费者就是消费消息的一方就是系统B,C,D。
削峰:
比如:系统A现在是一个电商系统,在双11的时候这种数据是很大的,一秒钟会有大量的数据去请求数据库进行写库,而数据库一定是有上限的,这个时候数据库就很容易崩,而且崩了会很麻烦。
现在,系统A还是正常的操作,只不过他不进行写库操作,而是将数据通过消息发送给MQ,这些消息由系统B来慢慢进行消费,将写库的峰值变得平缓。以保证数据库不会一下子崩掉。
4.MQ选型
MQ包括:ActiveMQ、RocketMQ、RabbitMQ、Kafka等
这些都称为MQ,至于他们之间的区别我就不给大家讲解了,大家随便一查资料就可以查到,而且,真正开发的时候选用什么MQ也不是你我说的算的,这是要经过谨慎的推敲才能定夺。
5.ActiveMQ的SpringBoot实现
-
第一步:创建Maven工程
-
第二步:引入配置(pom.xml)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
- 第三步:配置文件(application.yml)
amqConfig:
amqUrl: tcp://127.0.0.1:61616
user: admin
pwd: admin
- 第四步:两种方式
方式一:点对点的消息传递--队列(Queue)
特点:
1.每个消息只能有一个消费者,类似于1对1的关系。
2.无论消费者在生产者发送消息的时候是否处于运行状态,消费者都可以提取消息。
3.消息被消费后就销毁掉。
创建生产者和消费者:
public class ActiveMQUtil {
// MQ连接
@Value("${amqConfig.amqUrl}")
public String url;
// 用户名
@Value("${amqConfig.use}")
public String user;
// 密码
@Value("${amqConfig.pwd}")
public String pwd;
/**
*发送消息
*queue:队列名称
*msg:发送内容
**/
public static void SendMsg(String queue,String msg) throws JMSException {
// 1.创建连接工厂,传入url,用户名,密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(AMQ_USER, AMQ_PWD, AMQ_URL);
// 2.创建connection连接并启动连接
Connection connection = activeMQCommUtil.connectionFactory.createConnection();
connection.start();
// 3.创建Session(两个参数:transacted=是否开启事务,acknowledgeMode=签收模式)
Session session = connection.createSession(Boolean.TRUE.booleanValue(), Session.AUTO_ACKNOWLEDGE);
// 4.创建目的地(具体是队列(queue)还是主题(top))
Destination destination = session.createQueue(queue);
// 5.创建消息生产者
MessageProducer producer = session.createProducer(destination);
// 这里表示连接是非持久性的
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//6、创建消息并赋值
TextMessage textMessage = session.createTextMessage(); //String类型的
textMessage.setText(msg);
//7、发送消息
producer.send(textMessage);
session.commit();
// 关闭连接
producer.close();
session.close();
connection.close();
}
/**
* 接收消息
* @param Queue 队列名称
* @throws JMSException
*/
public static TextMessage ReceiveMsg(String queue) throws JMSException {
// 1.创建连接工厂,传入url,用户名,密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(AMQ_USER, AMQ_PWD, AMQ_URL);
// 2.创建connection连接并启动连接
Connection connection = activeMQCommUtil.connectionFactory.createConnection();
connection.start();
// 3.创建Session(两个参数:transacted=是否开启事务,acknowledgeMode=签收模式)
Session session = connection.createSession(Boolean.TRUE.booleanValue(), Session.CLIENT_ACKNOWLEDGE);
Destination destination = session.createQueue(Queue);
// 5.创建消息消费者
MessageConsumer consumer = session.createConsumer(destination);
// 6.接收消息
TextMessage msg = (TextMessage) consumer.receive();
session.commit();
// 7.关闭连接
consumer.close();
session.close();
connection.close();
return msg;
}
}
下面对以上代码简单讲解:可以看到创建生产者和消费者代码基本相同,代码注释已经比较详细。我只对一点做出一下讲解,两个方法中都有一个共同的参数“队列名称”,假设没有这个名称,系统A发出两个消息标为1号和2号,系统B和系统C谁也不知道这个消息到底1号是B的还是2号是B的。那现在有这个名称了之后,比如1号消息在名称为“1号”的队列中,我系统B中也配置一个这样的名称,这样系统B再去取消息的时候就直接到1号队列中去取。
方式二:发布订阅消息传递域——主题(topic)
特点:
1、生产者将消息发布到topic中,每个消息可以有多个消费者,属于1:N的关系
2、生产者和消费者之间有时间上的相关性。订阅某一个主题的消费者只能消费自它订阅之后发布的消息
3、生产者生产时,topic不保存消息,假如无人订阅就去生产,那就是一条废消息,所以,一般先启动消费者再启动生产者
大家理解上面的哪个这个的实现和上面就相差不大了只不过这个创建的是topic,上面的是queue。我也简单写一下
public class ActiveMQUtil {
// MQ连接
@Value("${amqConfig.amqUrl}")
public String url;
// 用户名
@Value("${amqConfig.use}")
public String user;
// 密码
@Value("${amqConfig.pwd}")
public String pwd;
/**
*发送消息
*topic:队列名称
*msg:发送内容
**/
public static void SendMsg(String topic,String msg) throws JMSException {
// 1.创建连接工厂,传入url,用户名,密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(AMQ_USER, AMQ_PWD, AMQ_URL);
// 2.创建connection连接并启动连接
Connection connection = activeMQCommUtil.connectionFactory.createConnection();
connection.start();
// 3.创建Session(两个参数:transacted=是否开启事务,acknowledgeMode=签收模式)
Session session = connection.createSession(Boolean.TRUE.booleanValue(), Session.AUTO_ACKNOWLEDGE);
// 4.创建目的地(具体是队列(queue)还是主题(top))
Destination destination = session.createTopic(topic);
// 5.创建消息生产者
MessageProducer producer = session.createProducer(destination);
// 这里表示连接是非持久性的
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
//6、创建消息并赋值
TextMessage textMessage = session.createTextMessage(); //String类型的
textMessage.setText(msg);
//7、发送消息
producer.send(textMessage);
session.commit();
// 关闭连接
producer.close();
session.close();
connection.close();
}
/**
* 接收消息
* @param topic 队列名称
* @throws JMSException
*/
public static TextMessage ReceiveMsg(String topic) throws JMSException {
// 1.创建连接工厂,传入url,用户名,密码
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(AMQ_USER, AMQ_PWD, AMQ_URL);
// 2.创建connection连接并启动连接
Connection connection = activeMQCommUtil.connectionFactory.createConnection();
connection.start();
// 3.创建Session(两个参数:transacted=是否开启事务,acknowledgeMode=签收模式)
Session session = connection.createSession(Boolean.TRUE.booleanValue(), Session.CLIENT_ACKNOWLEDGE);
Destination destination = session.createTopic(topic);
// 5.创建消息消费者
MessageConsumer consumer = session.createConsumer(destination);
// 6.接收消息
TextMessage msg = (TextMessage) consumer.receive();
session.commit();
// 7.关闭连接
consumer.close();
session.close();
connection.close();
return msg;
}
}
=========================================================================
通过两种方式的特点,大家根据实际情况权衡使用。
注意:发送和接收的消息类型要一致。都有哪些消息格式?
1.TxtMessage : 普通字符串消息,包含一个String
2.MapMessage :一个Map类型的消息,key为Strng类型,而值为Java基本类型
3.BytesMessage : 二进制数组消息,包含一个byte[]
4.StreamMessage : Java数据流消息,用标准流操作来顺序填充和读取
5.ObjectMessage :对象消息,包含一个可序列化的Java对象
========================================================================
关于ActiveMQ,就讲这么多了如果还不明白,大家可以去看一下这个作者讲的,讲的很不错的。
ActiveMQ详细使用(含高级篇)_activemq使用方法-CSDN博客
========================================================================
其他MQ:RocketMQ、RabbitMQ、Kafka等,留到后面讲解,因为目前我还没有真正在项目中用过,只是停留在练习阶段,这些其实都大差不差。
好了,就到这了。欢迎大家点赞,评论,关注。欢迎指出文章中的错误,万分感谢。
=========================stay hungry stay foolish===========================