RabbitMQ消息队列应用

1 RabbitMQ安装部署

RabbitMQ

这里是ErLang环境的下载地址
http://www.erlang.org/downloads

这是RabbitMQ环境、客户端、实例和说明文档的地址
http://www.rabbitmq.com/download.html

默认安装的Rabbit MQ 监听端口是:5672

进入目录
CD D:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.10\sbin

安装完以后erlang需要手动设置ERLANG_HOME 的系统变量。
set ERLANG_HOME=D:\Program Files\erl9.0

激活Rabbit MQ's Management Plugin
rabbitmq-plugins.bat  enable  rabbitmq_management

创建管理用户,所有的账号设置,都是区分大小写的,千万注意
rabbitmqctl.bat add_user BOEREms BOEREms1703

设置管理员,设置管理员前,BOEREms这个账号要先创建,否则设置就会失败。
rabbitmqctl.bat set_user_tags BOEREms administrator

设置权限
rabbitmqctl.bat set_permissions -p / BOEREms ".*" ".*" ".*"

查询用户
rabbitmqctl.bat list_users

查询vhosts
rabbitmqctl.bat list_vhosts

启动RabbitMQ服务: 
net stop RabbitMQ && net start RabbitMQ, 可以通过工具启动和关闭RabbitMQ服务

访问Rabbit Mq的管理控制台,使用刚才创建的账号登陆系统即可。
Rabbit MQ 管理后台,可以更好的可视化方式查看RabbitMQ服务器实例的状态。
http://localhost:15672

Virtual Host:UploadQueue
D:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.10\sbin>rabbitmq-plugins.ba
t enable rabbitmq_management
Plugin configuration unchanged.

Applying plugin configuration to rabbit@WIN-7BVDCM2AUG0... failed.
 * Could not contact node rabbit@WIN-7BVDCM2AUG0.
   Changes will take effect at broker restart.
 * Options: --online  - fail if broker cannot be contacted.
            --offline - do not try to contact broker.
D:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.10\sbin>rabbitmqctl status
Status of node 'rabbit@WIN-7BVDCM2AUG0'
Error: unable to connect to node 'rabbit@WIN-7BVDCM2AUG0': nodedown

DIAGNOSTICS
===========

attempted to contact: ['rabbit@WIN-7BVDCM2AUG0']

rabbit@WIN-7BVDCM2AUG0:
  * connected to epmd (port 4369) on WIN-7BVDCM2AUG0
  * epmd reports node 'rabbit' running on port 25672
  * TCP connection succeeded but Erlang distribution failed

  * Authentication failed (rejected by the remote node), please check the Erlang
 cookie


current node details:
- node name: 'rabbitmq-cli-17@WIN-7BVDCM2AUG0'
- home dir: C:\Users\Administrator
- cookie hash: /352utUNRCCPsDbXHre2Dw==
具体操作:将 C:\WINDOWS\.erlang.cookie   同步到RabbitMq 启动用户 例如win10:C:\Users\%USERNAME%\.erlang.cookie 

2 RabbitMQ原理简介

RabbitMQ结构示意
RabbitMQ内部结构

首先这个过程走分三个部分,1、客户端(生产消息队列),2、RabbitMQ服务端(负责路由规则的绑定与消息的分发),3、客户端(消费消息队列中的消息);由图可以看出,一个消息可以走一次网络却被分发到不同的消息队列中,然后被多个的客户端消费,那么这个过程就是RabbitMQ的核心机制,RabbitMQ的路由类型与消费模式。

RabbitMQ中间件分为服务端(RabbitMQ Server)和客户端(RabbitMQ Client),服务端可以理解为是一个消息的代理消费者,客户端又分为消息生产者(Producer)和消息消费者(Consumer)

  • 消息生产者(Producer):主要生产消息并将消息基于TCP协议,通过建立Connection和Channel,将消息传输给RabbitMQ Server,对于Producer而言基本就完成了工作。
  • 服务端(RabbitMQ Server):主要负责处理消息路由、分发、入队列、缓存和出列。主要由三部分组成:Exchange、RoutingKey、Queue
  1. Exchange:用于接收消息生产者发送的消息,有三种类型的exchange:direct, fanout,topic,不同类型实现了不同的路由算法;

A. direct exchange:将与routing key 比配的消息,直接推入相对应的队列,创建队列时,默认就创建同名的routing key。
B. fanout exchange:是一种广播模式,忽略routingkey的规则。
C. topic exchange:应用主题,根据key进行模式匹配路由,例如:若为abc则推入到所有abc相对应的queue;若为abc.#则推入到abc.xx.one ,abc.yy.two对应的queue。

  1. RoutingKey:是RabbitMQ实现路由分发到各个队列的规则,并结合Binging提供于Exchange使用将消息推送入队列;
  2. Queue:是消息队列,可以根据需要定义多个队列,设置队列的属性,比如:消息移除、消息缓存、回调机制等设置,实现与Consumer通信;
  • 消息消费者(Consumer):主要负责消费Queue的消息,同样基于TCP协议,通过建立Connection和Channel与Queue传输消息,一个消息可以给多个Consumer消费;
  • 关键名词说明:Connection、Channel、Binging等;
  1. Connection:是建立客户端与服务端的连接。
  2. Channel:是基于Connection之上建立通信通道,因为每次Connection建立TCP协议通信开销及性能消耗较大,所以一次建立Connection后,使用多个Channel通道通信减少开销和提高性能。
  3. Binging:是一个捆绑定义,将exchange和queue捆绑,定义routingkey相关策略。

3 RabbitMQ中的一些名词阐述与消息从投递到消费的整个过程

消息从投递到消费的整个过程(01)

从上图的标题中可以看到一些陌生的英文单词,让我们感觉一无所知,更无从操作,那么我给大家弄啦一个图片大家可以看下,或许对您理解这些新鲜的单词有所帮助。

消息从投递到消费的整个过程(02)

看过这些名词,之后,或许你还毫无头绪,那么我把消息从生产到消费的整个流程给大家说一下,或许会更深入一点,其中Exchange,与Queue都是可以设置相关属性,队列的持久化,交换器类型制定。

4 RabbitMQ中Exchange的类型

Direct Exchange是RabbitMQ默认的交换机模式,也是最简单的模式,根据key全文匹配去寻找队列。
类型有4种,direct,fanout,topic,headers。其中headers不常用,本篇不做介绍,其他三种类型,会做详细介绍。
那么这些类型是什么意思呢?就是Exchange与队列进行绑定后,消息根据exchang的类型,按照不同的绑定规则分发消息到消息队列中,可以是一个消息被分发给多个消息队列,也可以是一个消息分发到一个消息队列。具体请看下文。
介绍之初还要说下RoutingKey,这是个什么玩意呢?他是exchange与消息队列绑定中的一个标识。有些路由类型会按照标识对应消息队列,有些路由类型忽略routingkey。具体看下文。

  • Exchange类型direct
direct

他是根据交换器名称与routingkey来找队列的

rabbitMq_direct

第一个 X - Q1 就有一个 binding key,名字为 orange; X - Q2 就有 2 个 binding key,名字为 black 和 green。当消息中的 路由键 和 这个 binding key 对应上的时候,那么就知道了该消息去到哪一个队列中。
Ps:为什么 X 到 Q2 要有 black,green,2个 binding key呢,一个不就行了吗? - 这个主要是因为可能又有 Q3,而Q3只接受 black 的信息,而Q2不仅接受black 的信息,还接受 green 的信息。

Exchange类型direct

Note:消息从client发出,传送给交换器ChangeA,RoutingKey为routingkey.ZLH,那么不管你发送给Queue1,还是Queue2一个消息都会保存在Queue1,Queue2,Queue3,三个队列中。这就是交换器的direct类型的路由规则。只要找到路由器与routingkey绑定的队列,那么他有多少队列,他就分发给多少队列

  • Exchange类型fanout
fanout

这个类型忽略Routingkey,他为广播模式

Exchange类型fanout

Note:消息从客户端发出,只要queue与exchange有绑定,那么他不管你的Routingkey是什么他都会将消息分发给所有与该exchang绑定的队列中。

  • Exchange类型topic
topic

这个类型的路由规则如果你掌握啦,那是相当的好用,与灵活。他是根据RoutingKey的设置,来做匹配的,其中这里还有两个通配符为:

*,代表任意的一个词。例如topic.zlh.*,他能够匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, ....
#,代表任意多个词。例如topic.#,他能够匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, ....
Exchange类型topic

Note:这个图看上去很乱,但是他是根据匹配符做匹配的,这里我建议你自己做下消息队列的具体操作。

  • Headers Exchange
    headers 也是根据规则匹配, 相较于 direct 和 topic 固定地使用 routing_key , headers 则是一个自定义匹配规则的类型.
    在队列与交换器绑定时, 会设定一组键值对规则, 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列.

5 消息队列的消费与消息确认Ack

  • 消息队列的消费
消息队列的消费

Note:如果一个消息队列中有大量消息等待操作时,我们可以用多个客户端来处理消息,这里的分发机制是采用负载均衡算法中的轮询。第一个消息给A,下一个消息给B,下下一个消息给A,下下下一个消息给B......以此类推。

  • 为啦保证消息的安全性,保证此消息被正确处理后才能在服务端的消息队列中删除。那么rabbitmq提供啦ack应答机制,来实现这一功能。ack应答有两种方式:1、自动应答,2、手动应答

6 AMQP

AMQP(Advanced Message Queuing Protocol)协议是一个高级抽象层消息通信协议,RabbitMQ是AMQP协议的实现。它主要包括以下组件:

AMQ Protocol Model

  1. Server(broker): 接受客户端连接,实现AMQP消息队列和路由功能的进程。
  2. Virtual Host:其实是一个虚拟概念,类似于权限控制组,一个Virtual Host里面可以有若干个Exchange和Queue,但是权限控制的最小粒度是Virtual Host。
  3. Exchange:接受生产者发送的消息,并根据Binding规则将消息路由给服务器中的队列。ExchangeType决定了Exchange路由消息的行为,例如,在RabbitMQ中,ExchangeType有direct、Fanout和Topic三种,不同类型的Exchange路由的行为是不一样的。
  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建议客户端线程之间不要共用Channel,至少要保证共用Channel的线程发送消息必须是串行的,但是建议尽量共用Connection。
  9. Command:AMQP的命令,客户端通过Command完成与AMQP服务器的交互来实现自身的逻辑。例如在RabbitMQ中,客户端可以通过publish命令发送消息,txSelect开启一个事务,txCommit提交一个事务。

在了解了AMQP模型以后,需要简单介绍一下AMQP的协议栈,AMQP协议本身包括三层:

AMQP的协议栈
  1. Module Layer,位于协议最高层,主要定义了一些供客户端调用的命令,客户端可以利用这些命令实现自己的业务逻辑,例如,客户端可以通过queue.declare声明一个队列,利用consume命令获取一个队列中的消息。
  2. Session Layer,主要负责将客户端的命令发送给服务器,在将服务器端的应答返回给客户端,主要为客户端与服务器之间通信提供可靠性、同步机制和错误处理。
  3. Transport Layer,主要传输二进制数据流,提供帧的处理、信道复用、错误检测和数据表示。

7 RabbitMQ使用场景

场景1:单发送单接收

单发送单接收

使用场景:简单的发送与接收,没有特别的处理。

场景2:单发送多接收

单发送多接收

使用场景:一个发送端,多个接收端,如分布式的任务派发。为了保证消息发送的可靠性,不丢失消息,使消息持久化了。同时为了防止接收端在处理消息时down掉,只有在消息处理完成后才发送ack消息。

发送端和场景1不同点

  1. 使用“task_queue”声明了另一个Queue,因为RabbitMQ不容许声明2个相同名称、配置不同的Queue
  2. 使"task_queue"的Queue的durable的属性为true,即使消息队列durable
  3. 使用MessageProperties.PERSISTENT_TEXT_PLAIN使消息durable

接收端和场景1不同点

  1. 使用“task_queue”声明消息队列,并使消息队列durable
  2. 在使用channel.basicConsume接收消息时使autoAck为false,即不自动会发ack,由channel.basicAck()在消息处理完成后发送消息。
  3. 使用了channel.basicQos(1)保证在接收端一个消息没有处理完时不会接收另一个消息,即接收端发送了ack后才会接收下一个消息。在这种情况下发送端会尝试把消息发送给下一个not busy的接收端。

场景3:Publish/Subscribe

Publish/Subscribe

使用场景:发布、订阅模式,发送端发送广播消息,多个接收端接收。

发送端
发送消息到一个名为“logs”的exchange上,使用“fanout”方式发送,即广播消息,不需要使用queue,发送端不需要关心谁接收。

接收端

  1. 声明名为“logs”的exchange的,方式为"fanout",和发送端一样。
  2. channel.queueDeclare().getQueue();该语句得到一个随机名称的Queue,该queue的类型为non-durable、exclusive、auto-delete的,将该queue绑定到上面的exchange上接收消息。
  3. 注意binding queue的时候,channel.queueBind()的第三个参数Routing key为空,即所有的消息都接收。如果这个值不为空,在exchange type为“fanout”方式下该值被忽略!

场景4:Routing (按路线发送接收)

按路线发送接收

使用场景:发送端按routing key发送消息,不同的接收端按不同的routing key接收消息。

发送端和场景3的区别

  1. exchange的type为direct
  2. 发送消息的时候加入了routing key

接收端和场景3的区别
在绑定queue和exchange的时候使用了routing key,即从该exchange上只接收routing key指定的消息。

场景5:Topics (按topic发送接收)

按topic发送接收

使用场景:发送端不只按固定的routing key发送消息,而是按字符串“匹配”发送,接收端同样如此。

发送端和场景4的区别

  1. exchange的type为topic
  2. 发送消息的routing key不是固定的单词,而是匹配字符串,如".lu.#",匹配一个单词,#匹配0个或多个单词。

接收端和场景4的区别

  1. exchange的type为topic
  2. 接收消息的routing key不是固定的单词,而是匹配字符串。

推荐阅读更多精彩内容

  • 来源 RabbitMQ是用Erlang实现的一个高并发高可靠AMQP消息队列服务器。支持消息的持久化、事务、拥塞控...
    jiangmo阅读 8,303评论 2 34
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 105,385评论 12 126
  • rabbitMQ是一款基于AMQP协议的消息中间件,它能够在应用之间提供可靠的消息传输。在易用性,扩展性,高可用性...
    点融黑帮阅读 1,984评论 3 40
  • 关于消息队列,从前年开始断断续续看了些资料,想写很久了,但一直没腾出空,近来分别碰到几个朋友聊这块的技术选型,是时...
    预流阅读 274,199评论 41 612
  • 在不懂爱的年纪,我们奋不顾身着迷,在应该爱的年纪,我们用光了所有的勇气,在你之后,我再没有失去的东西。 今天注定是...
    茗莜阅读 247评论 3 4