一、RabbitMQ工作模式
1.Work queue工作队列模式
多个消费端共同消费同一个队列中的消息。
应用场景:
对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
生产者产生的消息会通过轮询的方式被多个消费端消费,所有消息只被消费一次。
2.订阅模式类型
在第一种类型中,只有3个角色:
P:生产者,也就是要发送消息的程序;
C:消费者,消息的接受者,会一直等待消息的到来;
Queue:消息队列,接收消息,缓存消息。
而在订阅模型中,多了一个exchange角色,而且过程略有变化:
P:生产者,也就是要发送消息的程序,不再发送消息到队列中,而是发送给X(交换机);
C:消费者,消息的接受者,会一直等待消息的到来;
Queue:消息队列,接收消息,缓存消息;
Exchange:交换机,图中的X。一方面,接收生产者发送的消息;另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或者将消息丢弃。到底如何操作,取决于Exchange的类型。Exchange有常见以下三种类型:
- Fanout:广播,将消息交给所有绑定到交换机的队列;
- Direct:定向,把消息交给符合指定routing key的队列;
- Topic:通配符,把消息交给符合routing pattern(路由模式)的队列。
Exchange (交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,后者没有符合路由规则的队列,那么消息会丢失。
3.Publish/Subscribe发布与订阅模式
(1)每个消费者监听自己的队列;
(2)生产者将消息发送给broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。
应用场景:
例如一个生产的处理结果需要同时发送给多台机器,这个时候就可以通过发布与订阅模式发送消息。
一个消息可以被多个消费者都收到。
发布订阅模式与工作队列模式的区别
(1)工作队列模式不用定义交换机,而发布/订阅模式需要定义交换机;
(2)发布/订阅模式的生产方面是面向交换机发送消息,工作队列模式的生产方是面向队列发送消息(底层使用默认交换机);
(3)发布/订阅模式需要设置队列和交换机的绑定,工作队列模式不需要设置,实际上工作队列模式会将队列绑定到默认的交换机。
4.Routing路由模式
- P:生产者,向Exchange发送消息,发送消息时,会指定一个routing key;
- X:Exchange(交换机),接收生产者的消息,然后把消息递交给与routing key完全匹配的队列;
- C1:消费者,其所在队列指定了需要routing key为error的消息;
- C2:消费者,其所在队列指定了需要routing key为info、error、warning的消息
路由模式特点:
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由Key);
- 消息的发送方在向Exchange发送消息时,也必须指定消息的Routingkey;
- Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routing Key与消息的Routing Key完全一致,才会接收到消息;
- Routing模式要求队列在绑定交换机时要指定routing key,消息会转发到符合routingkey的队列。
生产者与消费者的创建:
- 生产者:
(1) 生产者创建连接Connection,接着开启一个信道(即创建channel);
(2)声明(创建)交换机:包括交换机名称和交换机类型;
(3)声明(创建)队列:包括队列名称、是否定义持久化队列、是否独占本次连接、是否在不使用的时候自动删除队列;
(4)将队列绑定到交换机;
(5)发消息至交换机:指定交换机名称,路由key,其他属性,消息内容。
- 消费者:
(1) 生产者创建连接Connection,接着开启一个信道(即创建channel);
(2)声明(创建)交换机:包括交换机名称和交换机类型;
(3)声明(创建)队列:包括队列名称、是否定义持久化队列、是否独占本次连接、是否在不使用的时候自动删除队列;
(4)将队列绑定到交换机;
(5)创建消费者并设置消费处理;
(6)监听消息
5.Topic通配符模式
- 红色Queue:绑定的是usa.#,因此凡是以use.开头的routing key都会被匹配到;
- 黄色Queue:绑定的是#.news,因此凡是以.news结尾的routing key都会被匹配
模式说明:
Topic类型与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型和Exchange可以让队列在绑定Routing Key的时候使用通配符。
Routing Key 一般都是有一个或多个单词组成,多个单词之间以 “.” 分割,例如:item.insert
通配符规则:
#:匹配一个或多个词
* :匹配不多不少恰好一个词
举例:
item.#:能够匹配item.insert.abc 或者 item.insert
item.*:只能匹配 item.insert
6.模式总结
RabbitMQ工作模式:
(1)简单模式 HelloWorld
一个生产者、一个消费者,不需要设置交换机(使用默认的交换机)。
(2)工作队列模式Work Queue
一个生产者、多个消费者(竞争关系),不需要设置交换机(使用默认交换机)。
(3)发布订阅模式Publish/subscribe
需要设置类型为fanout的交换机,并且交换机和队列进行绑定,当发送消息到交换机后,交换机会将消息发送到绑定的队列。
(4)路由模式Routing
需要设置类型为direct的交换机,交换机和队列进行绑定,并且指定routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列。
(5)通配符模式Topic
需要设置类型为topic的交换机,交换机和队列进行绑定,并且指定通配符方式的routing key,当发送消息到交换机后,交换机会根据routing key将消息发送到对应的队列。
二、RabbitMQ常见的特性
1.过期时间TTL
TTL全称Time To Live(存活时间/过期时间)。过期时间TTL表示可以对消息设置预期的时间,在这个时间内都可以被消费者接收获取;过了之后消息将自动被删除。RabbitMQ可以对消息和队列设置TTL,目前有两种方法可以设置。
- 第一种方法是通过队列属性设置,队列中所有消息都有相同的过期时间;
- 第二种方法是对消息进行单独设置,每条消息TTL可以不同。
如果上述两种方法同时使用,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就称为dead message被投递到死信队列,消费者将无法收到该消息。
2.死信队列
DLX,全称为Dead-Letter-Exchange,可以称之为死信交换机,也有人称之为死信邮箱。当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换机中,这个交换机就是DLX,绑定DLX的队列就称之为死信队列。
消息变成死信,可能是由于以下的原因(面试高频):
- 队列消息长度到达限制;
- 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
- 原队列存在消息过期设置,消息到达超时时间未被消费。
DLX也是一个正常的交换机,和一般的交换机没有区别,它能在任何的队列上被指定,实际上就是设置某一个队列的属性。当这个队列中存在死信时,Rabbitmq就会自动地将这个消息重新发布到设置地DLX上去,进而被路由到另一个队列,即死信队列。
要想使用死信队列,只需要在定义队列地时候设置队列参数x-dead-letter-exchange指定交换机即可。
3.消息的可靠投递
生产方Confirm
在使用RabbitMQ的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ提供了两种方式用来控制消息的投递可靠性模式。
- confirm 确认模式
- return 退回模式
rabbitmq整个消息投递的路径为:
producer —> rabbitmq broker —>exchange —>queue —>consumer
- 消息从producer到exchange则会返回一个confirmCallback
- 消息从exchange—>queue投递失败则会返回一个returnCallback
利用这两个callback控制消息的可靠性投递
消费方确认 Ack
ack指Acknowledge,确认。表示消费端收到消息后的确认方式。
- 自动确认:acknowledge=“none”
- 手动确认:acknowledge=“manual”
其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应message从RabbitMQ的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动确认,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。
4.消费端限流
- prefetch属性设置消费端一次去拉取多少消息;
- 消费端的确认模式一定要设置为手动确认。acknowledge=“manual”
prefetch属性限制了每次消费者每次拉取多少条消息,当消息消费完并且手动确认后,才继续从broker中拉取消息。
5.延迟队列(面试高频)
5.1 什么是延迟队列
即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。
5.2 需求
(1)下单后,30分钟未支付,取消订单,回滚库存。
(2)新用户注册成功7天后,发送短信消息。
5.3 实现方式
(1)定时器:这种方式也比较常见,但是,不够优雅。如下单需求:一方面,它需要每隔一段时间,就去扫描整个订单表,然后对比时间找出符合条件的数据;另一方面,定时器时间间隔如果设置太短,则会频繁查表和执行相关程序,如果时间设置太长,则有的订单可能已经超过30分钟未支付而未被处理。
(2)延迟队列:如下单需求,可以设置消息隔多久后被消费,且可针对某个id去查订单表,根据订单支付状态再去做相关处理。
5.4 Rabbit实现延迟队列
RabbitMQ中并未提供延迟队列功能,但是可以使用TTL + 死信队列组合实现延迟队列的效果。
(1)延迟队列,指消息进入队列后,可以被延迟一定时间,再进行消费;
(2)RabbitMQ没有提供延迟队列功能,但是可以使用:TTL + DLX来实现延迟队列效果。
6.日志与监控
RabbitMQ默认日志存放路径:/var/log/rabbitmq/rabbit@xxx.log
7.消息追踪
在使用任何消息中间件的过程中,难免会出现某条消息异常丢失的情况。对于RabbitMQ而言,可能是因为生产者或消费者与RabbitMQ断开了连接,而它们与RabbitMQ又采用了不同的确认机制;也有可能是因为交换机与队列之间不同的转发策略;甚至是交换器并没有任何队列进行绑定,生产者又不感知或者没有采取相应的措施;另外RabbitMQ本身的集群策略也可能导致消息的丢失。这个时候就需要有一个较好的机制跟踪记录消息的投递过程,以此协助开发和运维人员进行问题的定位。
在RabbitMQ中可以使用Firehose和rabbitmq_tracing插件功能来实现消息追踪。
三、RabbitMQ应用问题
1. 消息可靠性保障
- 消息补偿机制
2. 消息幂等性保障
- 乐观锁解决方案
(幂等性指一次和多次请求某一资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果)