一、介绍
RabbitMQ (官网:http://www.rabbitmq.com) 是一个由erlang语言编写、实现AMQP协议的消息队列系统。
二、安装
本机(Windows)作为RabbitMQ服务器、需要先安装erlang语言
- 安装erlang语言 下载安装包exe文件 安装后需要配置环境变量
ERLANG_HOME = 安装目录
PATH = %ERLANG_HOME%\bin
cmd 输入指令erl 能出现正确信息则表示正确安装
- 下载RabbitMQ Server 选择exe文件 安装完配置环境变量
RABBIT_HOME =安装目录 e.g. D:\APP\RabbitMQ\rabbitmq_server-3.7.5
PATH = %RABBIT_HOME%\sbin
- 安装Rabbit插件
cmd 定位到%RABBIT_HOME%\sbin 执行 rabbitmq-plugins.bat enable rabbitmq_management
操作完成后 可以网页打开 http://localhost:15672 用默认账户guest 密码guest 登录 进行对系统的页面查看和操作
三、入门
消息流动图例
术语:
- 虚拟机(virtual host) : 单一中间件、为了实现多个分隔环境、提供虚拟机概念,虚拟机之间相互独立。默认虚拟机为"/"。
- 连接(Connection) : AMQP 的连接是long-lived的基于TCP的应用层协议,断开连接时,不能简单断开TCP连接,而要断开AMQP连接。
- 信道(Channel) : 一个应用需要与AMQP有多个连接,如果创建过多tcp连接,防火墙配置复杂。AMQP 连接使用多路复用信道,可以认为是共享单一TCP连接的轻量级连接。多线程的程序、一般不会共享channel使用。
- 消息 (Message) : RabbitMQ 以二进制块的形式接收、存储、传递数据。
- 交换器 (Exchange) : 交换器的作用类似邮件传输中的邮筒,消息直接传入交换器,然后将消息路由给0个或多个队列。路由算法取决于交换类型和绑定规则。
Name | Default pre-declared names |
---|---|
Direct exchange | (Empty string) and amq.direct |
Fanout exchange | amq.fanout |
Topic exchange | amq.topic |
Headers exchange | amq.match (and amq.headers in RabbitMQ) |
代码中声明一个交换器有多个方法
Exchange.DeclareOk exchangeDeclare(String exchange, String type) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange,
String type,
boolean durable,
boolean autoDelete,
boolean internal,
Map<String, Object> arguments) throws IOException;
Exchange.DeclareOk exchangeDeclare(String exchange,
BuiltinExchangeType type,
boolean durable,
boolean autoDelete,
boolean internal,
Map<String, Object> arguments) throws IOException;
参数
exchange/name 交换器名字
durable 是否为持久化交换器 默认false 决定了中间件重启后交换器是否存活
autoDelete 自动删除 默认不删除 设置为删除,则当最后一个连接队列断开连接后,交换器被删除
internal 内部交换器,设置为true,则交换器不能被客户端直接发布消息
arguments 插件及中间件的特殊功能...
- 生产 (Producing) : 既发送信息的过程。
- 生产者 (Producer) : 发送信息的程序。
- 队列 (Queue) : 信息(Message) 容器、大小受限于主机内存和存储空间。
Queue.DeclareOk queueDeclare() throws IOException;
Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments) throws IOException;
//同上一方法、但设置不等待的标志位、服务器不会返回消息
void queueDeclareNoWait(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException;
Queue.DeclareOk queueDeclarePassive(String queue) throws IOException;
参数
queue/name 队列名称
durable 持久化与否 服务器重启后队列是否存活
exclusive 排他性 true的话之有当前连接能使用
autoDelete 自动删除 服务器判定没有在使用状态则会删除队列
arguments 其他参数
使用该些方法声明队列、如果队列不存在、则会创建队列,若队列存在但是参数不一致、则会返回406错误
- 消费 (Consuming) : 既接收信息的过程。
- 生产者 (Consumer) : 接收消息的程序。
几种队列模式:
几种模式可以在官网查看demo 程序同时发布在github上
简单模式 (Hello World)
工作队列模式 (Work Queue)
发布/订阅模式 (Publish/Subscribe)
路由模式 (Routing)
通配符模式 (Topics)
远程调用模式 (RPC)
3.1 简单模式 (Hello World)
一个队列中 由一个生产者 产生消息 一个消费者 接收消息
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
3.2 工作队列模式 (Work Queue)
又称任务模式(Task Queue) 。一个队列,有一个生产者产生消息、多个消费者 竞争/共享 消息。将竞争资源的任务以消息的形式发送到队列中,多个工人(worker) 可以竞争任务、任务无需等待特定工人。默认情况下、队列采用轮播(round-robin)的方式来分发信息。
消息应答机制:默认情况、需要手动应答。既发送ack位 来告知队列,消息被正确消费掉了,如果连接断开、且没有发送ack位,则认为需要重新将该消息重新加入队列,重新寻找工人来处理该任务。
注意 ack 需要在同一channel中传输 (Acknowledgement must be sent on the same channel the delivery it is for was received on. 不能准确意会这句英文的意思) 否则会产生channel 级别的协议异常。
Linux下可以使用指令 sudo rabbitmqctl list_queues name messages_ready messages_unacknowledged 查看ack了的消息
Windows 下 rabbitmqctl.bat list_queues name messages_ready messages_unacknowledged
合理分配:
使用默认任务分配策略,任务进入队列后就分发给不同工人,假如只有两个工人,单数编号任务繁重,双数编号任务轻松,那么处理单数编号任务的工人就会忙个不停、另外一个工人相对空闲。
通过对channel设置 channel.basicQos(1); 设置为工人一时间只接收一个任务的分发,既队列不会预先对任务进行对该工人的分发;
3.3 发布/订阅模式 (Publish/Subscribe)
和工作队列模式不同、该模式下的消息可以由多个消费者接收处理。
交换器 (exchange) :
大部分情况下,一个生产者并不直接将消息传入队列中,而是将消息交给交换器,交换器在一定的规则下 选择消息要传入的队列 或者抛弃该次消息。交换器可以连接多个队列;
几种交换类型如下: direct, topic, headers, fanout
fanout : exchange会将消息路由到当前所有连接的队列中。
e.g. channel不需要声明队列、只需要声明交换器 channel.exchangeDeclare("logs", "fanout");
然后发布消息
channel.basicPublish( "logs", "", null, message.getBytes());//第一个参数为exchange名、第二个为routing Key,与下面的Binding相关,该参数在这个交换类型下被无视。
临时队列 : 前面例子中,我们不使用exchange,直接声明一个队列然后使用,队列需要一个指定的名字,双方必须相同才能正确交换信息。然而,使用exchange,队列名字可以不固定,甚至由队列服务器自己起名,然后连接到exchange上即可。
使用无参函数queueDeclare() 可以创建一个不持久化,自动删除的队列,且队列名字自动生成。
获取队列名
String queueName = channel.queueDeclare().getQueue();
3.4 路由模式 (Routing)
绑定 (Bindlings) : 交换器 与 队列 之间的关系、我们称之为绑定
channel.queueBind(queueName, "logs", "black"); //创建队列和交换器之间的绑定关系,命名为black
为了不与basicPublish() 中的routing key混淆,black这个参数、我们称之为 binding key。
direct 交换模式:
和fanout不同,消息在发布时会附带routing key,direct交换模式会检查队列和交换器之间的绑定关系,当 routing key 和 binding key完全一致时,才会将消息传入该队列中。
将前一个例子优化,如果我们需要关注日志级别,那么选择direct模式,创建多种binding,就可以把日志按级别分在不同队列中,方便查看。
值得注意的是:队列和交换器之间可以有多个绑定,例如可以绑定为info,也可以增加warning的绑定,实现查看warning和info级别的日志。一个同名绑定,也可以绑定到多个队列上。
3.5 通配符模式 (Topics)
路由模式可以实现简单的匹配,但是依然不够灵活。为此,使用通配符模式,可以做到更多灵活的选择。
通配符交换器(Topic exchange)
传入交换器的消息,需要有一个由多个字词组成,字词间用 . 来分割的routing key。这些字词是和消息相关的信息。最大支持255B(byte)。e.g. "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit".
Binding Key
binding key 也有所不同。
* (星号) 代表一个字词
# (hash 哈希) 代表0个或多个字词
如果routing key 满足 一个队列的一个或多个binding 那消息也只会进入该队列一次 !!!
3.6 远程过程调用RPC
略...