目录
初识MQ?
同步和异步通讯
同步
使用feign等远程技术,实现服务之间的通信
优点:时效性较强,可以立即得到结果
问题:
- 耦合度高
- 性能和吞吐能力下降
- 有额外的资源消耗
- 有级联失败问题
异步
使用mq实现服务之间的间接通信。
好处:
- 吞吐量提升:无需等待订阅者处理完成,响应更快速
- 故障隔离:服务没有直接调用,不存在级联失败问题
- 调用间没有阻塞,不会造成无效的资源占用
- 耦合度极低,每个服务都可以灵活插拔,可替换
- 流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件
常见mq技术对比
- RabbitMQ(重点)
- ActiveMQ
- RocketMQ(重点)
- Kafka
RabbitMq快速入门
安装步骤
dockerhub中搜索镜像https://hub.docker.com/_/rabbitmq
1.拉取镜像
docker pull rabbitmq:3.9-management
注意:带management的这种版本是带管理控制台的
2.创建容器
docker run --name mq -p 15672:15672 -p 5672:5672 -v mq-plugins:/plugins \
-d -e RABBITMQ_DEFAULT_USER=itcast -e RABBITMQ_DEFAULT_PASS=123321 rabbitmq:3.9-management
--name:容器名称
-p端口映射,15672管理控制台端口,5672是RabbitMQ通信端口
-v:数据卷挂载 /plugins是容器内部安装插件的目录
-d 后台
-e 设置环境变量,RABBITMQ_DEFAULT_USER和RABBITMQ_DEFAULT_PASS用来设置RabbitMq的用户名和密码
注意:如果没有指定rabbitmq的用户名和密码,自带guest,guest
docker run --name mq -p 15672:15672 -p 5672:5672 -v mq-plugins:/plugins -d rabbitmq:3.9-management
3.进入管理控制台
http://192.168.200.128:15672/
工作流程
producer->交换机->队列->comsumer
注:
producer(消息生产者)
rabbitmq
虚拟主机(/,及自带的)
交换机:对消息进行路由转发
队列:真正存储消息
comsumer(消息消费者)
工作模式
1.简单模式
producer->交换机->队列->comsumer
2.工作队列模式
工作队列,可以提高消息处理速度,避免队列消息堆积,多个消费者绑定到一个队列,同一条消息只会被一个消费者处理。
通过设置prefetch来控制消费者预取的消息数量
spring: rabbitmq: host: 192.168.214.130 #可变 port: 5672 # 端口 virtual-host: / # 虚拟主机 username: guest # 用户名 password: guest #密码 listener: simple: prefetch:1 # 每次只能得到一条消息,处理完成ACK之后,才能获取下一个消息
3.发布订阅模式
允许将同一消息发送给多个消费者。实现方式是加入了exchange(交换机)
常见exchange类型
- Fanout:广播。会将接收到的消息广播到每一个跟其绑定的queue
- Direct:路由
- Topic:话题
注意:exchange负责消息路由,而不是存储,路由失败则消息丢失
声明队列、交换机、绑定关系的Bean是什么?
- Queue
- FanoutExchange
- Binding
4.路由模式
将接收到的消息根据规则路由到指定的Queue
每一个Queue都与Exchange设置一个BindingKey
发布者发送消息时,指定消息的RoutingKey
Exchange将消息路由到BindingKey与消息RoutingKey一致的队列
Direct交换机与Fanout交换机的差异
Fanout交换机将消息路由给每一个与之绑定的队列
Direct交换机根据RoutingKey判断路由给哪个队列
如果多个队列具有相同的RoutingKey,则与Fanout功能类似
基于@RabbitListener注解声明队列和交换机有哪些常见注解?
- @Queue
- @Exchange
5.通配符模式
与DirectExchange类似,但routingKey可以是多个单词的列表,并且以 . 分割
Queue与Exchange指定BindingKey时可以使用通配符
#:代指0个或多个单词
*:代指一个单词
实现思路
并利用@RabbitListener声明Exchange、Queue、RoutingKey
在consumer服务中,编写两个消费者方法,分别监听topic.queue1和topic.queue2
在publisher中编写测试方法,向itcast. topic发送消息
交换机
交换机的作用
- 接收publisher发送的消息
- 将消息按照规则路由到与之绑定的队列
- 不能缓存消息,路由失败,消息丢失
- FanoutExchange的会将消息路由到每个绑定的队列
创建交换机
@Bean
public Exchange createFanoutExchange() {
return ExchangeBuilder.fanoutExchange("交换机名称").durable(true).build();}
创建队列
@Bean
public Queue createQueuel() {
return QueueBuilder.durable("队列名称").build();}
绑定交换机和队列
@Bean
public Binding xxxxx(@Qualifier("createFanoutExchange") Exchange exchange,
@Qualifier("createQueuel") Queue queue) {
return BindingBuilder. bind(queue). to(exchange).with("路由规则").noargs () ;
}
测试生产者消息
@SpringBootTest(classes = ProducerApplication.class)
public class TestSend {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendTopicExchange() {
rabbitTemplate.convertAndSend("交换机", "自定义路由规则", "消息内容");
}}
消息转换器
默认情况下Spring采用的序列化方式是JDK序列化。
- 数据体积过大
- 有安全漏洞
- 可读性差
配置JSON转换器
引入依赖
jackson-dataformat-xml
jackson-databind
配置消息转换器
方法一
启动类中添加一个Bean
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
方法二
专门写在一个配置类中,在启动类扫描即可
配置类
要加上@Configuration注解
@Bean public MessageConverter jsonMessageConverter(){ return new Jackson2JsonMessageConverter(); }
启动类和配置类不在同一模块下要进行扫描
@Import(XxConfig.class)
@Import({XxConfig.class,..})