?
??🎉🎉欢迎来到我的CSDN主页!🎉🎉
🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚
🌟推荐给大家我的博客专栏《RabbitMQ系列之入门级》。🎯🎯
🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁
????????MQ通常指的是消息队列(Message Queue),是一种用于在应用程序之间传递消息的通信方式。消息队列系统允许不同组件之间异步通信,通过在发送者和接收者之间引入队列,实现解耦和提高系统的可伸缩性。
????????在消息队列中,消息生产者将消息发送到队列,而消息消费者从队列中接收消息。这种异步通信的方式可以使系统中的各个组件独立运作,不需要直接依赖对方的状态,提高了系统的可维护性和可扩展性。
?????????MQ的应用场景包括但不限于,下面是几个典型的几个应用场景:
应用场景 | 说明 |
解耦系统组件 | 允许不同组件之间解耦,降低了组件之间的依赖性。 |
提高系统可靠性 | 如果某个组件暂时不可用,消息队列可以存储消息,待组件恢复正常后再处理这些消息。 |
异步通信 | 允许系统中的组件进行异步通信,提高了系统的响应性。 |
削峰填谷 | 在流量波动较大的场景中,消息队列可以用来平滑流量,防止系统峰值负载。 |
分布式系统协调 | 在分布式系统中,消息队列可以用来协调各个节点的工作。 |
????????RabbitMQ 是一个开源的消息队列系统,它遵循AMQP(Advanced Message Queuing Protocol)协议。它支持多种消息传递模式,包括点对点、发布/订阅和请求/响应。
????????RabbitMQ适用于需要高度灵活性和多样性消息传递模式的场景,同时它的性能和可靠性也是其特点之一。
????????Kafka 是一个分布式的、高吞吐量的消息系统,主要用于处理实时数据流。它的设计目标是具有高可用性和持久性,同时能够处理大规模的数据流。
?????????Kafka 适用于大规模的数据流处理,如日志收集、事件溯源、流式处理等场景。
????????ActiveMQ 是一个基于Java的开源消息中间件,支持多种协议,包括OpenWire、STOMP、AMQP等。它提供了丰富的功能,如持久化、事务、集群等。?
?????????ActiveMQ适用于需要与多种协议兼容,并且对消息传递的可靠性和事务有要求的场景。
?????????RocketMQ 是由阿里巴巴开发的分布式消息中间件,具有高吞吐、低延迟、高可用性的特点。它支持丰富的消息传递模式,如顺序消息、事务消息等。
?????????RocketMQ适用于高吞吐、低延迟要求的场景,尤其在电商、金融等领域有广泛应用。
????????SQS是亚马逊提供的托管消息队列服务,具有高可用性和弹性。它支持分布式架构,并提供了简单的API用于发送和接收消息。?
????????SQS适用于在AWS云上构建可扩展的、分布式的应用程序,无需担心消息队列的基础设施管理。?
????????Redis 是一个内存数据库,但它也提供了发布/订阅(Pub/Sub)功能。Redis的消息发布/订阅是基于频道的,允许多个订阅者监听同一个频道的消息。
?????????Redis的Pub/Sub适用于简单的消息发布和订阅场景,尤其是对于需要快速实现的小规模应用。
?????????这些是一些常见的MQ实现方式,选择合适的MQ取决于应用的具体需求,包括对性能、可靠性、消息传递模式的要求等。
????????假设我们在淘宝下了一笔订单后,淘宝后台需要做这些事情:
1. 消息通知系统:通知商家,你有一笔新的订单,请及时发货
2. 推荐系统:更新用户画像,重新给用户推荐他可能感兴趣的商品
3. 会员系统:更新用户的积分和等级信息
createOrder(...) {
// 完成订单服务
doCreateOrder(...);
// 调用其他服务接口
sendMsg(...);
updateUserInterestedGoods(...);
updateMemberCreditInfo(...);
}
? ? ? ? 如上图所示,我们订单系统创建订单的时候自身需要2s的处理时间,然后要向三个系统分别发送请求并且等待他们的响应一个系统服务需要等待2是,一共需要8s。
存在问题:
????????过度耦合:如果后面创建订单时,需要触发新的动作,那就得去改代码,在原有的创建订单函数末尾,再追加一行代码
????????缺少缓冲:如果创建订单时,会员系统恰好处于非常忙碌或者宕机的状态,那这时更新会员信息就会失败,我们需要一个地方,来暂时存放无法被消费的消息
? ? ? ? 这时候我们使用一个消息中间件,来实现解耦和缓冲的功能
? ? ? ? ?使用一个消息中间件可以让我们的订单系统不必等待我们其他三个系统的响应而去处理其他的事务只需要等待2s,其他系统的响应让他在后台运行,就算响应的时间很长也影响不到订单系统的工作。
????????RabbitMQ的主要组成部分如下:
Producer(生产者):
- 生产者是消息的发送方,负责产生并发送消息到RabbitMQ的消息队列中。生产者通常将消息发布到一个特定的交换机。
Exchange(交换机):
- 交换机是消息的分发中心,负责将消息路由到一个或多个消息队列。RabbitMQ支持不同类型的交换机,包括直接交换机、主题交换机、扇出交换机等,以支持不同的消息分发策略。
Queue(消息队列):
- 队列是消息的存储位置,生产者将消息发送到队列,而消费者从队列中接收消息。消息在队列中等待被消费。
Binding(绑定):
- 绑定是交换机和队列之间的关联关系。它定义了消息如何从交换机路由到特定的队列。生产者通过将消息发送到交换机,而消费者通过从队列接收消息来实现消息的传递。
Consumer(消费者):
- 消费者是消息的接收方,负责从消息队列中获取消息并进行处理。消费者订阅一个或多个队列,以接收交换机路由到这些队列的消息。
Virtual Host(虚拟主机):
- 虚拟主机是RabbitMQ中的逻辑隔离单位,允许在同一物理服务器上创建多个相互独立的消息中间件环境。每个虚拟主机拥有自己的交换机、队列、绑定等。
Connection(连接):
- 连接是生产者和消费者与RabbitMQ之间的网络连接。一个连接可以包含多个通道(Channel),每个通道代表一个独立的会话。连接的建立和管理是由RabbitMQ客户端库处理的。
通道(通道):
- 通道是在连接内部的逻辑通信信道,生产者和消费者通过通道与RabbitMQ进行交互。通道的使用可以减轻连接的开销,提高性能。
? ? ? ? 我们现在我们的虚拟机中拉取我们的一个RabbitMQ的一个镜像文件,版本拉取management的。
指令:docker pull rabbitmq:management?
? ? ? ? 我们拉取了rabbitmq镜像文件之后我们需要去创建一个RabbitMq的容器去运行使用。
创建的命令如下:
docker run -d \
--name my-rabbitmq \
-p 5672:5672 -p 15672:15672 \
--hostname my-rabbitmq-host \
-e RABBITMQ_DEFAULT_VHOST=my_vhost \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
--restart=always \
rabbitmq:management
?指令说明:
--hostname:主机名(RabbitMQ的一个重要注意事项是它根据所谓的 “节点名称” 存储数据,默认为主机名)
-e:指定环境变量:
RABBITMQ_DEFAULT_VHOST:默认虚拟机名
RABBITMQ_DEFAULT_USER:默认的用户名
RABBITMQ_DEFAULT_PASS:默认用户名的密码
?
? ? ? ? 我们创建完我们的RabbitMq容器之后我们可以输入指令:?docker logs my-rabbitmq 进行一个查看日志。
? ? ? ? ?我们在网页进行访问rabbitmq的后台界面,进行登陆进入。账号和密码在创建容器时就设置好了。
? ? ? ? ?登陆进去的首页。
? ? ? ? ?这就说明我们的rabbitmq的安装和部署已经成功。
? ? ? ? 我们在登陆进入的后台首页中右侧的Virtual host中选择我们创建容器时的主机。
? ? ? ? ?我们点击页面的Admin进入其选项,新建一个用户用于我们后续的一个操作实现,我们一般不用admin进行登陆。
? ? ? ? 我们创建好新的用户之后,还要点击进入该用户给该用户分配我们的主机。?下面是一些后台界面的功能说明。
? ? ? ? 打开我们的开发工具IDEA进行创建对应的项目用户实现功能。我们新建一个空的项目。
? ? ? ? 创建好空项目之后我们在这个空项目下创建两个Spring模块一个是发布者,一个是消费者。
? ? ? ? ?我们创建好项目以及我们的两个模块后,我们在两个模块下的配置文件转换成yml的格式,以及对其配置文件进行配置。
server:
port: 9999
spring:
rabbitmq:
# 安装rabbitMq容器的虚拟机的ip地址
host: 192.168.52.130
# Rabbitmq的用户账号
username: yxspring
# Rabbitmq的用户密码
password: 123456
# Rabbitmq的容器的端口号
port: 5672
# RabbitMq的主机
virtual-host: my_vhost
server:
port: 8888
spring:
rabbitmq:
# 安装rabbitMq容器的虚拟机的ip地址
host: 192.168.52.130
# Rabbitmq的用户账号
username: yxspring
# Rabbitmq的用户密码
password: 123456
# Rabbitmq的容器的端口号
port: 5672
# RabbitMq的主机
virtual-host: my_vhost
? ? ? ? 我们配置完我们的配置文件之后,我们先发送消息到我们的RabbitMq中去,编写对应的所需的代码。在我们的发布者模块下创建一个配置类。
package com.yx.publisher;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@SuppressWarnings("all")
public class RabbitConfig {
@Bean
public Queue firstQueue() {
return new Queue("firstQueue");
}
}
? ? ? ? 我们在发布者编写完我们的配置类之后在创建一个controller类用于发送我们的消息到我们的RabbitMQ中。(注:我们没有配置类就无法向我们的RabbitMq中发送信息以及生成队列)
package com.yx.publisher;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* com.yx.publisher
*
* @author 君易--鑨
* @site www.yangxin.com
* @company 木易
* @create 2024/1/19
*/
@RestController
public class TestController {
// 注入AmqpTemplate
@Autowired
private AmqpTemplate amqpTemplate;
// 编写一个请求方法
@RequestMapping("/send1")
public String send1(){
// 向交换机发送消息
amqpTemplate.convertAndSend("firstQueue","木易");
return "木易";
}
}
? ? ? ? 我们直接启动我们的发布者服务,在网页去访问我们控制类中的方法。我们多访问几次多刷新几次
? ? ? ? 我们再去到RabbitMq后台管理界面去查看。?
? ? ? ? 我们之前只是向我们的RabbitMq中发送消息,但我们并没有去接收RabbitMQ的消息,接下来就是接收消息。
package com.yx.consumer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@SuppressWarnings("all")
@Slf4j
@RabbitListener(queues = "firstQueue") //接收的队列
public class Receiver {
@RabbitHandler
public void process(String msg) {
log.warn("接收到:" + msg);
}
}
? ? ? ? ?我们接听的队列一定要存在否则我们访问的时候回报错,然后我们运行消费者服务,我们会自动的接受我们的RabbitMQ的消息。
?????????上述图片的是我们消费者接收消息控制台输出对应的结果,我们基本的接受信息的操作就实现了。
? ? ? ? ?我们接下来在我们的发布者中创建一个User实体类,然后我们发起一个请求将我们的实体对应传输过去。
package com.yx.publisher;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@SuppressWarnings("all")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User{
private String username;
private String userpwd;
}
? ? ? ? 我们创建好实体类之后我们再去到我们的Controller类中去编写一个方法发送我们的实体对应到我们的RabbitMQ上去。在编写方法之前我们先去RabbitConfig配置类中去编写一个发送实体对象的队列。
? ? ? ? 我们在对应的消费者服务中编写一个专门接受实体对象的类,并且在消费者服务者编写对应的实体对象进行接受。
package com.yx.consumer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@SuppressWarnings("all")
@Slf4j
@RabbitListener(queues = "secondQueue") //接收的队列
public class PojoReceiver {
@RabbitHandler
public void process(User user) {
log.warn("接收到:" + user);
}
}
? ? ? ? 编写完成之后我们重启我们的发布者服务,我们在网页测试发送实体对象的请求方法。
? ? ? ? 我们的对象想要发送过去首先我们的对象实体类要实现序列化接口,我们在重新启动发布者服务调用对应的请求方法
?
?
? ? ? ? 我们启动消费者进行接收信息结果会报错。
? ? ? ? 报错的原因是因为发送的对象的名称和包名需要一致,我们如何解决呢?
? ? ? ? 我们在发送之前将我们的实体对象转换成json的格式发送过去,再在消费者那边将json格式转换为实体对象的格式。
? ? ? ? 消费者在对应的接收类中我们对其先注入ObjectMapper类,再将传输过来的json格式转换为实体对象。
? ? ? ? 我们再重启两个服务再进行对应的接口测试。?
? ? ? ? 由上图可知我们成功的解决了传输实体对象的问题。?
🎉🎉本期的博客分享到此结束🎉🎉
📚📚各位老铁慢慢消化📚📚
🎯🎯下期博客博主会带来新货🎯🎯
🎁三连加关注,阅读不迷路?!🎁