- rabbitMQ整体架构模型是什么样子的
- rabbitMQ消息是如何流转的
- rabbitMQ安装与使用
- 命令行与管控台
- rabbitMQ消息生产与消费(Java)
- rabbitMQ交换机详解
- rabbitMQ队列 绑定 虚拟主机 消息
JMX
rabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数据,rabbitMQ使用的就是Erlang语言来编写的,并且rabbitMQ是基于AMQP协议。
通过不同调用api将消息发送到队列服务器中,然后后端可以调用api消费队列服务器中的消息,形成一个生产者与消费者模型
rabbitMQ优点:
- 开源,性能优秀,稳定性保障
- 提供可靠性消息投递模式confirm
- 与springAMQP完美整合,API丰富
- 集群模式丰富,基于表达式配置,HA模式,镜像队列模型
- 在保证数据不丢失的前提下做到高可用性,高可靠性
Q:rabbitMQ高性能的原因
- Erlang语言最初在于交换机领域的架构模式,这使得rabbitMQ在blocker之间进行数据交互,数据同步的性能是十分优秀的
- Erlang的优点就是:Erlang有着与原生socket一样的延时。
APC 基于socket,socket延时十分低,主要的考量目标就是延时以及响应 关注MQ的延时怎么样 性能怎么样 吞吐量怎么样。
什么是AMQP高级消息队列协议
AMQP定义:是具有现代特征的二进制协议,是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。
换句话说AMQP协议就是一个规范,在编写MQ时候只要依据MQ规范编写即可。
virtual host虚拟主机 (比较上层的路由)
exchange 交换机(AMQP核心)生产者直接将消息投递到exchange中,经历三个过程
- 查找服务器,建立链接,设置地址以及端口号
- 投递到哪一个virtual host
- 然后就是投递到哪一个exchange
rabbitMQ最主要目的就是解耦和的。
consumer application是从message Queue中获取数据的,然后整个MQ最主要的一点就是exchange与message queue之间的绑定关系,不然的话message queue无法从exchange中获取消息。
AMQP核心概念
- Server:又称为broker,接受客户端的链接,实现AMQP实体服务
- connection:连接,应用程序与broker的网络连接
- channel:网络通道,几乎所有的操作都是在channel中进行的,channel是进行消息读写的通道。客户端可建立多个channel,每个channel代表一个会话任务。
- message:消息 服务器和应用程序之间传递的数据,由properties和body组成的。properties可以对消息进行修饰,比如消息的优先级,延时等高级特性;body则就是消息体内容。
- virtual host:虚拟地址,用于逻辑隔离,最上层的消息路由。一个virtual host中可以有若干个exchange和queue,同一个virtual host里面不能有相同名称的exchange或者是queue。
- 这是一个逻辑概念,并非像物理隔离那样子,将每一个数据大小都标明,使用逻辑隔离,可能会导致每个数据库内存大小并不一致。
- exchange:交换机,接收消息,根据路由键转发消息到绑定的队列中。队列与exchange中有绑定关系
- binding:exchange和Queue之间的虚拟连接,binding可以包含routing key
- routing key:一个路由规则,虚拟机可以用它来确定如何路由一个指定的信息
- Queue:也称message queue,消息队列,保存消息并将它们转发给消费者
rabbitMQ整体架构是什么样子
生产者只需要关系将消息放置在哪一个exchange就可以了,不需要关心具体投放在哪一个队列
消费者只需要关系消息是在哪一个队列就可以了,不需要关心从哪里来的。
绑定就是路由。
exchange是有路由策略的,就是所谓的rootingkey,我们发送message时候需要携带两个重要的点,第一个就是选择哪一个exchange,第二个就是携带rootingkey。然后通过exchange和队列建立绑定关系,借助你的rootingkey将消息路由到你指定的队列之中,消费者只需要监听该队列就可以了。
rabbitMQ安装
erlang安装包,rabbit基于erlang开发的。
安装Linux必要的包。
下载rabbit必须安装包
配置文件修改
最好使用3.6x版本。
Q:如何在实际生产环境中,更新rabbitMQ服务器版本,更改集群。
- 技术调研,版本是否需要更新,性能是否更好。
- 高性能的测试。
- 上线时候,也需要进行灰度上线。100个节点首先只发布1 2个节点,在生产环境中先跑几个月观察一下。然后就是再整体上线。
有一点注意的就是erlang版本需要与rabbitMQ相互对应。在Linux中下载rabbitMQ时候,首先下载erlang和密钥包。
在实际工作中,通常都是配置导出,然后再配置导进就可以了。
消息生产与消费
- ConnectionFactory:获取链接工厂 。 需要配置相关信息,MQ节点的地址端号。
- Connection:一个链接
- Channel:数据通信信道,可发送和接收消息。
- Queue:具体的消息存储队列
- Producer&Consumer生产和消费者。
在编写过程之前,需要在pom.xml导入相关依赖的包。在<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>中进行依赖配置。
Producer
//1.创建一个ConnectionFactory,并进行配置。
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory .setHost("192.168.11.76");
connectionFactory .setPort(5672);//默认端口号
connectionFactory .setVirtualHost("/");//设置虚拟主机 这个虚拟主机是在管控台自己设置的
//2.通过连接工厂创建连接
Connection connection = connectionFactory .newConnection();
//3.通过Connection创建一个Channel 这个是通信最关键的一部分。
Channel channel = connection.createChannel();
//4.通过 channel发送数据,消息组成部分主要就是props和body,props主要就是修饰消息一些附加属性。
//指定exchange和rootingKey 传送的是bytes数组
//channel.basicPublish(exchange,routingKey,props,body);
String msg = "SADAS00";
channel.basicPublish("","test001",null,msg.getBytes());
//5.记得关闭相关连接
channel.close();
connection.close();
consumer
//1.创建一个ConnectionFactory,并进行配置。
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory .setHost("192.168.11.76");
connectionFactory .setPort(5672);//默认端口号
connectionFactory .setVirtualHost("/");//设置虚拟主机 这个虚拟主机是在管控台自己设置的
//2.通过连接工厂创建连接
Connection connection = connectionFactory .newConnection();
//3.通过Connection创建一个Channel 这个是通信最关键的一部分。
Channel channel = connection.createChannel();
//最少有一个队列存放信息,然后consumer才能监听这个队列,接收消息,消费消息
//4.声明一个队列 通过channel操作
//channel.queueDeclare(queue,durable,exclusive,autoDelete,arguments);
//queue是队列名字,可以是一个String
//durable是否持久化,持久化的好处就是服务器即使重启,这个队列也是不会丢失
//exclusive表示独占的方式,在某些场景下还是很有好处的,比如说顺序消费,独占就是表示这个队列只有这
//个channel可以监听,其他的连接都不可以监听,就是相当于加了一把锁。目的就是保证消息的顺序消费
//有一种情况就是消费者是集群ABC,通过负载均衡可能使A消费3条,B消费3条,C消费4条,但是不能保证ABC消费信息的进度是有顺序的,就是谁要在谁前面先完成的问题。使用独占方式。
//autoDelete 就是队列如果没有绑定关系的时候,就会自动删除
//arguments扩展参数
channel.queueDeclare("test001",true,false,false,null);
//5.创建消费者,表示这个消费着是建立在channel这个连接之上的。
QueueingConsuumer queueingConsuumer = new QueueingConsuumer(channel);
//6.设置channel,channel.basicConsumer(queue,autoAck,callback);
//autoAck是否自动签收,blocker和消费端进行监听数据,发送数据,消费数据。当blocker发送消息给消费端时候,消费端就会马上发送一个Ack消息,表示该消息已经收到。可以设定手工Ack
//callback就是代表消费者对象
channel,channel.basicConsumer("test001",true,queueingConsuumer);
//获取消息 Delivery就是对MQ进行了一个Java级别的封装
//Delivery delivery = queueingConsuumer.nextDelivery();//如果没有消息的话就阻塞
while(true){
Delivery delivery = queueingConsuumer.nextDelivery();
String msg = new String(delivery.getBody);
System.err.println("消费端"+msg);
//这里有一个重要的对象 Envelope envelope = delivery.getEnvelope();
//可以获取到关于队列信息,自己手写Ack时候,也要通过这个对象实现。
}
有一个值得注意的是,一开始要先运行一次消费端代码,这个目的就是为了创建(声明相对应的队列)。
Q:rootingKey 生产者与消费者之间的队列是通过如何来绑定的。
A:虽然你没有设置exchange,但是只要生产者中channel设定的rootingKey值与消费者队列名称相同的话,就可以自动绑定了。
因为rabbitMQ在生产者发消息的时候,必须要指定一个exchange,如果你不指定exchange或者exchange为空的话,他就会走第一个exchange,AMQP default的exchange,它的路由规则就是 根据你的rootingKey来查找MQ队列中是否有相对应的名字。如果有就把消息路由到指定的队列。
exchange交换机
exchange:接收消息,并且根据路由键转发消息所绑定的队列。
name:名称
type:交换机属性 direct fanout headers
durability 是否需要持久化
Auto Delete 当最后一个绑定到exchange上的队列删除后,自动删除该exchange
internal:当前exchange是否用于rabbitmq内部使用 默认是false
arguments:扩展参数 用于扩展amqp协议自制定化使用
direct exchange
所有发送到direct exchange 的消息被转发到routekey中指定的queue
注意:direct模式可以使用rabbitmq自带的exchange:default exchenge,所以不需要将exchenge进行任何绑定操作,消息传递时候,routekey必须完全匹配时才会被队列接收,否则消息会被抛弃。
//1.创建一个ConnectionFactory,并进行配置。
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory .setHost("192.168.11.76");
connectionFactory .setPort(5672);//默认端口号
connectionFactory .setVirtualHost("/");//设置虚拟主机 这个虚拟主机是在管控台自己设置的
connectionFactory.
//2.通过连接工厂创建连接
Connection connection = connectionFactory .newConnection();
//3.通过Connection创建一个Channel 这个是通信最关键的一部分。
Channel channel = connection.createChannel();
//4.通过 channel发送数据,消息组成部分主要就是props和body,props主要就是修饰消息一些附加属性。
//指定exchange和rootingKey 传送的是bytes数组
//channel.basicPublish(exchange,routingKey,props,body);
String exchangeName = "test_direct_exchange";
String routingKey = "test.direct";
//5.发送
String msg = "SADAS00";
channel.basicPublish(exchangeName ,routingKey ,null,msg.getBytes());
//5.记得关闭相关连接
channel.close();
connection.close();
//1.创建一个ConnectionFactory,并进行配置。
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory .setHost("192.168.11.76");
connectionFactory .setPort(5672);//默认端口号
connectionFactory .setVirtualHost("/");//设置虚拟主机 这个虚拟主机是在管控台自己设置的
//网络中断后,设置是否重连
connectionFactory.setAutomaticRecoveryEnable(true);
//设置重连的时间
connectionFactory.setNetworkRecoveryInterval(3000);
//2.通过连接工厂创建连接
Connection connection = connectionFactory .newConnection();
//3.通过Connection创建一个Channel 这个是通信最关键的一部分。
Channel channel = connection.createChannel();
//最少有一个队列存放信息,然后consumer才能监听这个队列,接收消息,消费消息
//4.声明一个队列 通过channel操作
//channel.queueDeclare(queue,durable,exclusive,autoDelete,arguments);
//queue是队列名字,可以是一个String
//durable是否持久化,持久化的好处就是服务器即使重启,这个队列也是不会丢失
//exclusive表示独占的方式,在某些场景下还是很有好处的,比如说顺序消费,独占就是表示这个队列只有这
//个channel可以监听,其他的连接都不可以监听,就是相当于加了一把锁。目的就是保证消息的顺序消费
//有一种情况就是消费者是集群ABC,通过负载均衡可能使A消费3条,B消费3条,C消费4条,但是不能保证ABC消费信息的进度是有顺序的,就是谁要在谁前面先完成的问题。使用独占方式。
//autoDelete 就是队列如果没有绑定关系的时候,就会自动删除
//arguments扩展参数
//声明交换机以及队列
String exchangeName = "test_direct_exchange";
String exchangeType = "direct";
String queueName = "test_direct_queue";
String routingKey = "test.direct";
channel.exchangeDeclre(exchangeName ,exchangeType ,true,false,false,null);
channel.queueDeclare(queueName ,false,false,false,null);
//绑定交换机以及队列
channel.queueBind(exchangeName,queueName ,routingKey );
//5.创建消费者,表示这个消费着是建立在channel这个连接之上的。
QueueingConsuumer queueingConsuumer = new QueueingConsuumer(channel);
//6.设置channel,channel.basicConsumer(queue,autoAck,callback);
//autoAck是否自动签收,blocker和消费端进行监听数据,发送数据,消费数据。当blocker发送消息给消费端时候,消费端就会马上发送一个Ack消息,表示该消息已经收到。可以设定手工Ack
//callback就是代表消费者对象
channel,channel.basicConsumer(queueName ,true,queueingConsuumer);
//获取消息 Delivery就是对MQ进行了一个Java级别的封装
//Delivery delivery = queueingConsuumer.nextDelivery();//如果没有消息的话就阻塞
while(true){
Delivery delivery = queueingConsuumer.nextDelivery();
String msg = new String(delivery.getBody);
System.err.println("消费端"+msg);
//这里有一个重要的对象 Envelope envelope = delivery.getEnvelope();
//可以获取到关于队列信息,自己手写Ack时候,也要通过这个对象实现。
}
direct:必须保证routingkey名称完全一致才可以。如果想通过queueName匹配到routingKey的话需要重新设置一下生产者端的routingKey。
fanout exchange
- 不处理任何的路由键,所以你的路由器是fanout模式的话,设置不设置routingkey也没有所谓。它只需要简单的将队列绑定到交换机上就可以了。
- fanout交换机转发消息是最快的,因为不需要进行任何的匹配。
- 发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。
个人理解:就是一个交换机可以绑定多个队列,一个队列可以被多个交换机绑定,找到相对应的交换机时候,就通过routingKey进行匹配,找到相对应的队列,rabbitMQ默认的路由规则就是通过queueName匹配的,其他都是通过routingKey匹配的 熟称路由规则。
Topic Exchange
- 所有发送到topic exchange 的 消息被转发到所有关心routingkey中指定的queue上
- exchange将routingkey和某一个topic进行模糊匹配,此时队列需要绑定一个topic
关系之间的重新设置的话,就要进行解锁绑定。
bingding-绑定
exchange和exchange,queue之间的连接关系(可以通过不同的routingkey将消息重exchange转发到另一个exchange)
binding中可以包含routingKey或者参数
queue
消息队列,实际存储消息数据
durability:是否持久化 durable 是 transient 否
auto delete 代表当最后一个监听被移除后,该queue会自动删除
Message消息
服务器和应用程序之间传递的数据
本质上就是一段数据,由properties 和 payload (body)组成
常用属性:delivery mode headers(自定义属性) 送达模式(持久化或者非持久化 自己自定义 自己获取)
其他属性
content_type content-_encoding priority
是不是可以通过设定优先级来确保优先级高的数据先被消费,优先级低的数据后被消费
不能的,集群模式,一定没有办法保证消息按照一定的序列被消费的,这里的话就涉及了顺序消息的概念。
correlation_id reply_to expiration message_id
correlation_id在业务中可能作为唯一的id使用,业务和timestamp拼接。 ACK 消息路由 幂等 都会用到
reply_to 做重回队列,就是消息失败后,可以返回到哪一个队列
expiration表示消息过期的时间。
timestamp jtype userid appid cclusterid
下面主要是props部分的设置。
virtual host-虚拟主机
虚拟地址,用于进行逻辑隔离,最上层的消息路由
一个virtual host 里面可以有若干个exchange和queue
同一个virtual host 里面不能有相同名称的exchange或queue