谈谈RabbitMQ

消息通信

常见的web服务之间的通信机制有两种 ,同步和异步。
同步方法有RMI、Hessin、Burlap、HTTP invoker,虽然同步通信比较简单,但是存在如下问题:服务需要等待,耦合度高!而异步通信就不存在这些问题,它无需等待,web服务只要将消息发送后就可以马上继续执行;对象对象和解耦;位置独立,消息发起者只需要知道 消息服务器的位置就可以发送消息,消息接收也无需知道发起者的具体位置,它只需要知道消息服务器在哪里就能获取消息。
常见的异步消中间件有kafaka、RabbitMQ、ZeroMQ、ActiveMQ,他们之间各有优势,该用哪一种需要看实际需求而定。本文只介绍RabbitMQ的一些知识。

AMQP协议

介绍RabbitMQ就必须先介绍AMQP协议,因为RabbitMQ是它的一种实现而已。

AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。

AMQP模型

这里写图片描述

  1. Server(broker): 接受客户端连接,实现AMQP消息队列和路由功能的进程,可以理解为邮局。

  2. Virtual Host:其实是一个虚拟概念,类似于权限控制组,一个Virtual Host里面可以有若干个Exchange和Queue,当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue等,就好比于tomcat中webapps目录下可以部署多个web项目。

3.Exchange:接受生产者发送的消息,并根据Binding规则将消息路由给服务器中的队列,就好比邮递员。

4.Message Queue:消息队列,用于存储还未被消费者消费的消息,就好比于邮箱。

5.Message: 由Header和Body组成,Header是由生产者添加的各种属性的集合,包括Message是否被持久化、由哪个Message Queue接受、优先级是多少等,就好比于邮箱里面的信件。而Body是真正需要传输的APP数据,就像信件里面的信纸。

6.Binding:Binding联系了Exchange与Message Queue。Exchange在与多个Message Queue发生Binding后会生成一张路由表,路由表中存储着Message Queue所需消息的限制条件即Binding Key。当Exchange收到Message时会解析其Header得到Routing Key,Exchange根据Routing Key与Exchange Type将Message路由到Message Queue。Binding Key由Consumer在Binding Exchange与Message Queue时指定,而Routing Key由Producer发送Message时指定,两者的匹配方式由Exchange Type决定,就好比于邮件上面的地址。

7.Connection:连接,对于RabbitMQ而言,其实就是一个位于客户端和Broker之间的TCP连接。

8.Channel:信道,仅仅创建了客户端到Broker之间的连接后,客户端还是不能发送消息的。需要为每一个Connection创建Channel,AMQP协议规定只有通过Channel才能执行AMQP的命令。一个Connection可以包含多个Channel。之所以需要Channel,是因为TCP连接的建立和释放都是十分昂贵的,如果一个客户端每一个线程都需要与Broker交互,如果每一个线程都建立一个TCP连接,暂且不考虑TCP连接是否浪费,就算操作系统也无法承受每秒建立如此多的TCP连接,可以简单的理解为线程池中的一个个线程。

RabbitMQ模型

上文说到RabbitMQ只是AMQP的一种实现,只不过是使用了ErLang来实现的,因此RabbitMQ的模型和AMQP模型也就大同小异了。

这里写图片描述

理解了AMQP的模型,对于RabbitMQ模型也就理解了。

这里的RabbiMQ Server就是broker,RoutingKey也就是Binding的意思,对于Exchange,RabbitMQ总共有4种不同的常用类型(比JMS多了整整一倍啊!),fanout、direct、topic、headers。前三种不做解释了,简单的总结一下前三种:fanout——往每家每户都发送邮件;direct——往某一户人家发送邮件;topic——往姓张的家里发送邮件。详细的可以看一下我写的前一篇RabbitMq文章

headers type:上文提到了一个Message由headers和body组成,body代表消息实体,headers则代表了消息的各种属性。因此headers类型的Exchange是根据发送的消息内容中的headers属性进行匹配。

消息确认(Confirm)机制

RabbitMQ的消息确认机制是为了确保消息发送者知道自己发布的消息被正确接收,如果没有收到确认时就会认为消息发送过程发送了错误,此时就会马上采取措施,以保证消息能正确送达(类似于HTTP的建立连接时的确认答复)。
具体做法如下:
当RabbitMQ发送消息以后,如果收到消息确认,才将该消息从Quque中移除。如果RabbitMQ没有收到确认,如果检测到消费者的RabbitMQ链接断开,则RabbitMQ 会将该消息发送给其他消费者;否则就会重新再次发送一个消息给消费者。

持久化

RabbitMQ的一大特性就是支持消息持久化。但是Rabbit MQ默认是不持久队列、Exchange、Binding以及队列中的消息的,这意味着一旦消息服务器重启,所有已声明的队列,Exchange,Binding以及队列中的消息都会丢失,这是因为支持持久化会对性能造成较大的影响。

什么时候需要持久化?

1.我们根据自己的需求对它们进行持久化(具体方法可以参考官方的API)。

注意:消息是存在队列里的,如果要使得消息能持久化,就必须先使队列持久化。

2.内存紧张时,需要将部分内存中的消息转移到磁盘中。

消息如何刷到磁盘?

1.写入文件前会有一个Buffer,大小为1M,数据在写入文件时,首先会写入到这个Buffer,如果Buffer已满,则会将Buffer写入到文件(未必刷到磁盘)。
2.有个固定的刷盘时间:25ms,也就是不管Buffer满不满,每个25ms,Buffer里的数据及未刷新到磁盘的文件内容必定会刷到磁盘。
3.每次消息写入后,如果没有后续写入请求,则会直接将已写入的消息刷到磁盘:使用Erlang的receive x after 0实现,只要进程的信箱里没有消息,则产生一个timeout消息,而timeout会触发刷盘操作。

RPC

RabbitMQ中也支持RPC,具体实现如下:

1.客户端发送请求(消息)时,在消息的属性中设置两个值replyTo(用于告诉服务器处理完成后将通知我的消息发送到这个Queue中)和correlationId(此次请求的标识号,服务器处理完成后需要将此属性返还,客户端将根据这个id了解哪条请求被成功执行了或执行失败)

2.服务器端收到消息并处理

3.服务器端处理完消息后,将生成一条应答消息到replyTo指定的Queue,同时带上correlationId属性

4.客户端之前已订阅replyTo指定的Queue,从中收到服务器的应答消息后,根据其中的correlationId属性分析哪条请求被执行了,根据执行结果进行后续业务处理

这里写图片描述

老板让秘书去买东西,告诉秘书将买到的东西送到他家门口保卫处,并写上自己的名字。
他家门口保卫处——replyTo。
自己的名字——correlationId。

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