简单聊一下RabbitMQ

RabbitMQ凭借着其异步解耦的两大核心特性在分布式系统应用中大放异彩。虽然在平时工作中经常用到,但是却很少去深入研究。这两天看了一下《RabbitMQ实战》,总结了平时不太注意的几个点。

1、Erlang

RabbitMQ是用Erlang语言开发的,听说Erlang在处理通信和并发时很是擅长。

2、AMQP

RabbitMQ是一个实现了AMQP(Advanced Message Queuing Protocol,即高级消息队列协议)标准的代理服务器。

AMQP协议模型

3、信道

应用程序和RabbitMQ之间创建一条TCP连接,才能消费或者发布消息。信道是建立在“真实的”TCP连接内的虚拟连接。AMQP命令都是通过信道发送出去的。在一条TCP连接上创建多少条信道是没有限制的。

4、虚拟主机

vhost,相当于一个mini版的RabbitMQ服务器,拥有自己的队列、交换机和绑定。可以给用户分配每个vhost的不同权限。虚拟主机配合用户权限一般用于隔离系统、模块或者环境等。

5、队列

(1)消费者通过以下两种方式从特定的队列中接收消息

  • basic.consume命令:消费后自动接收下一条消息。信道置为接收模式直到取消订阅。推荐用法。
  • basic.get命令:每次调用接收下一条消息。相当于订阅->获得消息->取消订阅,影响性能。

(2)如果至少有一个消费者订阅了队列的话,消息会立即发送给这些订阅的消费者。如果消息到达了无人订阅的队列,消息会在队列中等待,一旦有消费者订阅到该队列,那么队列上的消息就会发送给消费者。

(3)当队列拥有多个消费者时,队列收到的消息将以循环(round-robin)的方式发送给消费者。每条消息只会发送给一个订阅的消费者。

(4)消费者接收到的每一条消息都必须进行确认,这样RabbitMQ才能安全地把消息从队列中删除。可以通过basic.ack命令显式地向RabbitMQ发送一个确认。或者将auto_ack参数设置为true,这样一旦消费者接收消息,RabbitMQ会自动视其确认了消息。

(5)如果消费者收到一条消息,然后确认之前从RabbitMQ断开连接(或者从队列上取消订阅),RabbitMQ会认为这条消息没有分发,然后重新分发给下一个订阅的消费者。这样做可以保证你的应用程序崩溃了,消息也会被发送给另一个消费者进行处理。另一方面,如果应用程序有bug而忘记确认消息的话,RabbitMQ将不会给该消费者发送更多消息了。

(6)在收到消息后如果想要明确拒绝而不是确认的话,有两种选择

  • 把消费者从RabbitMQ服务器断开连接,这样RabbitMQ就会自动重新把消息入队并发送给另一个消费者。不推荐。
  • 如果RabbitMQ是2.0.0及以上的版本,那就使用basic.reject命令:如果reject命令的requeue参数设置为true,RabbitMQ会将消息重新发送给下一个订阅的消费者;如果设置为false的话RabbitMQ会将消息从队列中移除,而不会把它发送给新的消费者。

(7)新的版本支持”死信“(dead letter)队列,用来存放那些被拒绝而不重入队列的消息。如果应用程序想自动从死信队列中获益的话,需要使用reject命令并将requeue参数设置成false。

6、交换器

服务器会根据Routing key将消息从交换器路由到队列。四种交换机类型

  • headers:允许匹配消息的header而非路由键,除此之外和direct完全一致但是性能差很多,所以不太实用。
  • direct:如果路由键匹配的话,消息就被投递到对应的队列。RabbitMQ有一个空白字符串名称的默认交换器,当声明一个队列时,它会自动绑定到默认的交换器,并以队列名称作为路由键。
  • fanout:这种类型的交换器会将收到的消息广播到绑定的队列上。这允许你对单条消息做不同方式的处理。
  • topic:跟direct很像,不过它的路由规则可以使用通配符。

7、持久化

每个队列和交换器都有durable属性(默认为false),它决定了RabbitMQ是否需要在崩溃或者重启之后重新创建队列或者交换器。

消息想要持久化,必须满足三个条件

  • 把它的投递模式选项设置为2(持久)
  • 发送到持久化的交换器
  • 到达持久化的队列

代价:写入磁盘的持久化日志文件,性能影响很大。

8、事务

在AMQP中,在把信道设置成事务模式后,你通过信道发送那些想要确认的消息,之后还有多个其它AMQP命令。这些命令是执行还是忽略,取决于第一条消息发送是否成功。使用事务会降低消息吞吐量,也会使生产者应用程序产生同步。发送方确认模式可以保证消息投递:它是异步的,当确认消息最终收到的时候,生产者应用的回调方法就会被触发来处理。

9、其它

(1)当消费者处理消息失败的时候怎么办?我们系统目前采用的一种方式是:创建一个消息补偿队列和消息失败记录表,当消费失败时,将消息内容、业务方法、失败次数丢到补偿队列中,然后该队列的消费者根据消息内容和业务方法通过反射调用处理,如果还是失败就继续丢回补偿队列。如果达到了指定的失败次数,则插入失败记录表,并且告警到企业微信提示人工检查处理。

(2)在分布式系统中,不可避免地会出现分布式事务,如果保证强一致性难免也会带来复杂性和影响吞吐量,这个时候往往会保证最终一致性即可。使用MQ保证消息投递的最终一致性也成为了解决分布式事务的常用方案。

未完,待续...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章