RabbitMq学习:
1.概念:rabbitMq是基于amqp的高级协议,运行速度和socket一致。
2.核心组件:
broker:服务器(server)
exchange:
1.direct:直接匹配
2.topic:模糊匹配
#:关键字后面多词模糊匹配
*: 关键字后面只匹配一个词
通常是消费者这边使用通配符的匹配生产者
3.fanout:不需要处理路由件,不走任何的路由key,会将消息发送到指定交换机的指定queue上。
性能最好,因为不走路由
channel:通道,所有操作都在通道完成
message: 由两部分组成:properties(消息设置)、body(具体内容)
queue:
routingkey:
virtual host:虚拟地址,用于逻辑隔离,最上层的消息路由。同一个virtual host中不能够有相同名字的exchange或者queue
3.提供者和消费者
提供者:只关心exchange和routingKey
消费者:只关心queue和routingKey以及exchange0
如果消息提供者没有指定exchange的话,就会默认使用AMQP DEFAULT exchange 根据routingKey去完全匹配一个queue,
如果能够匹配就将消息路由出去,否则删除message。
4.消息可靠性投递方案
1.消息入库机制,在发送消息之前,将消息入库并这只状态为发送中。消息正常发送到rabbit server并返回confirm,将数据库消息发送状态为完成。设置分布式任务等待server的confirm返回值,如果在规定的时间没有返回值则重新retry 发送消息。在规定的次数内完成发送并收到server的confirm返回值,将数据库状态设置为完成。如在规定的次数未完成,则将数据库消息发送状态设置为失败。后期全部发送完毕之后统一处理(日志信息显示失败原因!rpc调用会出现网络原因)
2.延迟发送:消息提供者首先入库消息的部分信息,同时起连个发送服务,一个立即发送,一个延时发送(具体延时时间看具体情况)新建一个callback listener监听消费者端发送回来的confirm messge以及提供者延时发送的消息,如果能够监听到,则将这条信息入库。如果不能够监听到则提示提供者重新发送信息。当延时发送的消息被监听到,回去数据库中查询是否要这条消息的消费记录,如果有则不运行这条延时发送的消息,如果在规定的时间中依然没有这条消息的消费记录,则消费延时消息。
5.如何避免消息的重复消费?
使用消息提供者端的comfirm listener来监听消息返回值。
返回值为ack or noack
6.如何判断消息提供者提供的消息是否被消费?
使用return listener来监听消息是否被消费。
7.自定义consumer extends DedfaultConsumer就能够实现消费了。
8.消费端的限流处理方式:
为什么要进行消费端限流?
巨量的消息推送过来,单个客户端无法同时处理这些海量的数据,可能会导致客户端宕机,故需要进行消费端限流配置。RabbitMq提供了一种qos功能,在非自动确认消息的前提下,如果一定数目的消息未被确认前,不进行消费新的消息
原生api:defaultConsumer.basicqos(int perfetchSize,int perfetchCount,bollean gloal)
perfetchSize: 0 表示不指定上限,其他的数字表示指定上限。
perfetchCount:表示客户端(消费者端)能够同时接收的最大消息数量
gloal: true 便是针对整个channel,faalse针对的是 consumer
注意事项:要想使用qos进行限流,必须将ack设置为手动ack
Demo:
/**
* 声明一个exchange
*/
channel.exchangeDeclare(exchangeName,exchangeType,true);
/**
* 声明一个队列
*/
channel.queueDeclare(queueName,true,false,false,null);
channel.queueBind(queueName,exchangeName,routingKey);
/** 指定提供者的数量 指定同时过来多少条,其余的要等到这些ack后再过来
是否全局化true针对channel false consumer
* int prefetchSize, int prefetchCount, boolean global
*/
channel.basicQos(0,3,false);
DefaultConsumer consumer = new MyConsumer(channel);
/**
* String queue, boolean autoAck, Consumer callback
*/
channel.basicConsume(queueName,false,consumer);
9.ack和手动ack
ack是消费者是否成功消费消息的标识,其中关键配置参数为:Boolean autoAck true = 自动ack false=手动ack
provider可以用 confirm listener来监听消息是否被消费,同时给出相关处理。
手动ack需要在自定义的消费者中,直接根据某个条件来判断其是否ack。
basicAck() 为成功消费消息!
basicNack() 手动消费失败!
10.死信队列 dlx dead letter exchange
什么情况下消息会进入死信队列?
1.消息没有被成功消费,同时在手动nack处理时参数requeue被设置为false,这时候消息会进入死信队列。
2.消息时间过期,TTL过期,超过设置的最大消息存活时间。
3.超过设置qos中的最大队列数:perftchSize
死信队列的声明:在消费端声明,和其他的交换机声明方式一致。死信队列数据存放在队列里面。
11.自定义adpter中必须实现一个默认方法名:handleMessage
12.springboot 注解接收消息:
@RabbitHandler
@RabbitListener(指定queue、指定exchange、指定routingkey)
onmessage(@Payload 目标属性 @Headers 需要的属性内容)
从properties里面读取数据只需要使用${springboot.rabbitMq.exchange.name} 表达式形式处理就ok了
13.集群的恢复。
1.正常的A、B(b为master)关闭停机。
先启动A,30s后启动B。或者先启动B再启动A即可。
2.A B同时停机(机房掉电等原因)。
只需要在30s内启动A B两个节点即可。
3.A(备用节点)挂了,B(主节点)没有挂。
启动B(主节点),然后再主节点上执行rabbitmqctl forget_cluster-node-A 解除A节点和本集群的关系,然后再新启动一个节点作为从(备)节点加入到该集群即可
4.A(备用节点)没挂,B(主)节点挂掉了。
先启动A节点,然后再A节点上启动命令:rabbitmqctl forget_cluster-node-B_offline 就可以在主节点不在线的情况下解除主节点和该集群的绑定,然后再将A作为主节点,最后新启动一个新的节点作为从节点加入该集群即可。