• Rabbit MQ 基础入门


    简介

    RabbitMQ 简介

    在介绍 RabbitMQ 之前实现要介绍一下MQ,MQ 是什么?MQ 全称是 Message Queue,可以理解为消息队列的意思,简单来说就是消息以管道的方式进行传递。RabbitMQ 是一个实现了AMQP(Advanced Message Queuing Protocol)高级消息队列协议的消息队列服务,用 Erlang 语言的。

    为什么选择 RabbitMQ

    1.RabbitMQ 实现了 AMQP 标准的消息服务器;
    2.可靠性,RabbitMQ 的持久化支持,保证了消息的稳定性;
    3.高并发,RabbitMQ 使用了 Erlang 开发语言,Erlang 是为电话交换机开发的语言,天生自带高并发光环,和高可用特性;
    4.集群部署简单,正是应为 Erlang 使得 RabbitMQ 集群部署变的超级简单;
    5.社区活跃度高,根据网上资料来看,RabbitMQ 也是首选;

    RabbitMQ 的模型架构是什么?

    RabbitMQ 整体上是一个生产者与消费者模型,主要负责存储和转发消息。我们可以把 RabbitMQ 想象成一个快递站,我们需要邮寄包裹需要把包裹送到快递站,快递站会暂存并最终通过快递员来把包裹送到收件人的手上。RabbitMQ 就好比是快递站和快递员组成的一个系统。
    在这里插入图片描述

    AMQP 协议是什么?

    AMQP 协议本身包括三层
    1.Module Layer:位于协议最高层,主要定义客户端调用的命令。例如 Queue.Declare 命令声明一个队列或者使用 Basic.Consume 订阅消费一个队列中的消息。
    2.Session Layer:位于中间层,主要负责将客户端的命令发送给服务器,再将服务器的应答返回给客户端。主要为客户端与服务器之间的通信提供可靠性同步机制和错误处理。
    3.Transport Layer:位于最底层,主要传输二进制数据流,提供帧的处理、信道复用、错误检测和数据表示等。

    AMQP 其本质还是一个通信协议,通信协议都会涉及报文交互,从简单了说 AMQP 本身就是应用层协议是 TCP 协议的数据部分。从复杂了说 AMQP 是通过协议命令进行交互的,可以看作是一系列命令的集合操作。

    AMQP 常用命令

    名称 是否包含内容体 对应客户端中的方法 简要描述
    Connection.start factory.newConnection 建立连接相关
    Connection.Start-Ok 同上 同上
    Connection.Tune 同上 同上
    Connection.Tune-Ok 同上 同上
    Connection.Open 同上 同上
    Connection.Open-Ok 同上 同上
    Connection.Close connection.close 关闭连接
    Connection.Close-Ok 同上 同上
    Channel.Open connection.openChannel 开启信道
    Channel.Open-Ok 同上 同上
    Cbannel.Close channel.close 关闭信道
    Channel.Close-Ok 同上 同上
    Exchange.Declare channel.exchangeDeclare 声明交换器
    Exchange.Declare-Ok 同上 同上
    Exchange.Delete channel.exchangeDelete 删除交换器
    Exchange.Delete-Ok 向上 同上
    Exchange.Bind channel.exchangeBind 交换器与交换器绑定
    Exchange.Bind-Ok 同上 同上
    Exchange.Unbind channel.exchangeUnbind 交换器与交换器解绑
    Exchange.Unbind-Ok 同上 同上
    Queue.Declare channel.queueDeclare 声明队列
    Queue. Declare-Ok 同上 同上
    Queue.Bind channel.queueBind 队列与交换器绑定
    Queue.Bind-Ok 同上 同上
    Queue.Purge channel.queuePurge 清除队列中的内容
    Queue.Purge-Ok 同上 同上
    Queue.Delete channel.queueDelete 删除队列
    Queue.Delete-Ok 同上 向上
    Queue.Unbind channel.queueUnbind 队列与交换器解绑
    Queue.Unbind-Ok 同上 同上
    Basic.Qos channel.basicQos 设置未被确认消费的个数
    Basic.Qos-Ok 同上 同上
    Basic.Consume channel.basicConsume 消费消息(推模式)
    Ba siιCo nsurne-Ok 同上 同上
    Basic.Cancel channel.basicCancel 取消
    Basic.Cancel-Ok 同上 同上
    Basic.Publish channel.basicPublish 发送消息
    Basic.Return 未能成功路由的消息返回
    Basic.Deliver Broker 推送消息
    Basic.Get channel.basicGet 消费消息(拉模式)
    Basic.Get-Ok 同上 同上
    Basic.Ack channel.basicAck 确认
    Basic.Reject channel.basicReject 拒绝(单条拒绝)
    Basic.Recover channel.basicRecover 请求 Broker 重新发送未被确认的消息
    Basic.Recover-Ok 同上 同上
    Basic.Nack channel.basicNack 拒绝(可批量拒绝)
    Tx.Select channel. txSelect 开启事务
    TX.Select-Ok 同上 同上
    Tx.Cornmit channel. txCommit 事务提交
    TX.Commit-Ok 同上 同上
    Tx.Rollback channel.txRollback 事务回滚
    TX.Rollback-Ok 同上 同上
    Confirrn Select channel.confinnSelect 开启发送端确认模式
    Confinn.Select-Ok 同上 同上

    概念

    生产者和消费者

    Producer:生产者,就是投递消息的一方。
    生产者创建消息然后发布到 RabbitMQ 中。消息一般可以包含两部分:标签和消息体。在实际应用中消息体一般是一个带有业务逻辑结构的数据,比如一个 json 字符串。消息的标签用来表述这条消息,比如一个交换器的名称和一个路由键。生产者将消息发布到 RabbitMQ,RabbitMQ 之后会根据标签把消息发送到感兴趣的消费者。

    Consumer:消费者,就是接收消息的一方。
    消费者连接到 RabbitMQ 服务器并订阅到队列上。当消费者消费一条消息时只是消费的消息体而不是标签,标签在消息路由过程中会丢弃。存入到队列中的只是消息体,消费者只会消费消息体。

    Broker:消息中间件的服务节点。
    一个 Broker 可以简单的看作是一个 RabbitMQ 服务节点或者 RabbitMQ 服务实例。
    在这里插入图片描述

    队列

    Queue:队列,是 RabbitMQ 的内部对象,用于存储消息。
    在这里插入图片描述
    RabbitMQ 中消息只能存储在队列中,主题模式只是每个消费者申请一个队列和交换器绑定了。RabbitMQ 的生产者生产消息并最终投递到队列中,消费者可以从队列中获取消息并消费。
    多个消费者也可以订阅到同一个队列,这时队列中的消息会被平均分摊(Round-Robin 轮询)给多个消费者进行处理,而不是每个消费者都收到同样的消息并处理,叫做工作队列。
    在这里插入图片描述

    交换器、路由键、绑定

    Exchange:交换器,生产者将消息发送到 Exchange,由交换器将消息路由到一个或者多个队列中。
    在这里插入图片描述

    RabbitMQ 常用交换器类型有:fanout、direct、topic、headers 这四种
    1.fanout:它会把所有发送到该交换机的消息路由到所有与该交换机绑定的队列中。
    2.direct:它会把消息路由到那些 bindingKey 和 RoutingKey 完全匹配的队列中。
    3.topic:topic 类型的交换器在匹配的规则上进行了扩展,与 direct 相似,但是匹配规则有些不同。
      3.1.RoutingKey 为一个点号 “.” 分隔的字符串如 “com.rabbitmq.client”、“java.util.lang”、""。
      3.2.BindingKey 和 RoutingKey 一样也是点号 “.” 分隔的字符串。
      3.3.BindingKey 中可以存在两种特殊字符串 “" 和 “#”,用于做模糊匹配,其中 "” 用于匹配一个单词,"#" 用于匹配多规格单词(可以是零个)。
    在这里插入图片描述
    以上图为例
      1.路由键为 “com.rabbitmq.client” 的消息会被路由到 Queue1 和 Queue2;
      2.路由键为 “com.hidden.client” 的消息只会被路由到 Queue2 中;
      3.路由键为 “com.hidden.demo” 的消息只会被路由到 Queue2 中;
      4.路由键为 “java.rabbitmq.demo” 的消息只会路由到 Queue1 中;
      5.路由键为 “java.util.concurrent” 的消息将会被丢弃或者返回给生产者(需要设置 mandatory 参数),因为它没有匹配任何路由键;

    **RoutingKey:**路由键,生产者将消息发给交换机的时候,一般会指定一个路由键,用来指定消息路由规则,而这个路由键需要与交换机类型和绑定键联合使用才能生效。
    **Binging:**绑定,RabbitMQ 中通过绑定将交换器与队列关联起来,在绑定的时候一般会指定一个绑定键,这样 RabbitMQ 就知道如何正确的将消息路由到队列了。
    在这里插入图片描述
    生产者将消息发送给交换器时,需要一个 RoutingKey ,当 BindingKey 和 RoutingKey 相匹配时,消息会被路由到对应的队列中。

    RabbitMQ 运转流程,消息从生产者发出到消费者这一过程要经历一些什么?

    了解了 RabbitMQ 的架构模型,我们来回顾下整个流程。
    1.生产者发送消息
      1.1.生产者连接到 RabbitMQ Broker ,建立一个连接(Connection) ,开启一个信道(Channel)
      1.2.生产者声明一个交换器,并设置相关属性,比如交换机类型、是否持久化等
      1.3.生产者声明一个队列井设置相关属性,比如是否排他、是否持久化、是否自动删除等
      1.4.生产者通过路由键将交换器和队列绑定起来
      1.5.生产者发送消息至 RabbitMQ Broker ,其中包含路由键、交换器等信息
      1.6.相应的交换器根据接收到的路由键查找相匹配的队列
      1.7.如果找到,则将从生产者发送过来的消息存入相应的队列中
      1.8.如果没有找到,则根据生产者配置的属性选择丢弃还是回退给生产者
      1.9.关闭信道
      1.10.关闭连接

    public class RabbitMqReview {
    	private static final String EXCHANGE_NAME = "test_exchange_any_type";
    	private static final String QUEUE_NAME = "hello_word";
    
    	public static void main(String[] args) throws IOException, TimeoutException {
    		// 声明连接工厂
    		ConnectionFactory factory = new ConnectionFactory();
    		// 使用连接工厂赋值 userName、password、host、vHost、port 等等
    		factory.setUsername("userName");
    		factory.setPassword("userName");
    		factory.setHost("host");
    		factory.setVirtualHost("vhost");
    		factory.setPort(5672);
    		// 创建连接
    		Connection conn = factory.newConnection();
    		System.out.println("RabbitMqReview 获取到的链接是: " + conn);
    		// 创建通道
    		Channel channel = conn.createChannel();
    		System.out.println("RabbitMqReview 获取到的通道是: " + channel);
    		// 声明一个交换器
    		channel.exchangeDeclare(EXCHANGE_NAME, "direct");
    		// 声明一个队列
    		// channel.queueDeclare(QUEUE_NAME, false, false, false, null);
    		// 发送消息
    		String message = "Hello Word!";
    		channel.basicPublish(EXCHANGE_NAME, "routingKey", MessageProperties.TEXT_PLAIN, message.getBytes());
    		// 关闭资源
    		channel.close();
    		conn.close();
    	}
    }
    

    2.消费者接收消息
      2.1.消费者连接到 RabbitMQ Broker,建立一个连接(Connection),开启一个信道(Channel)
      2.2.消费者向 RabbitMQ Broker 请求消费相应队列中的消息
      2.3.等待 RabbitMQ Broker 回应并投递相应队列中的消息,消费者接收消息
      2.4.消费者确认(ack) 接收到的消息
      2.5.RabbitMQ 从队列中删除相应己经被确认的消息
      2.6.关闭信道
      2.7.关闭连接

    public class RabbitMqRecReview {
    	private static final String EXCHANGE_NAME = "test_exchange_any_type";
    	private static final String QUEUE_NAME    = "hello_word";
    
    	public static void main(String[] args) throws IOException, TimeoutException {
    		// 声明连接工厂
    		ConnectionFactory factory = new ConnectionFactory();
    		// 使用连接工厂赋值 userName、password、host、vHost、port 等等
    		factory.setUsername("userName");
    		factory.setPassword("userName");
    		factory.setHost("host");
    		factory.setVirtualHost("vhost");
    		factory.setPort(5672);
    		// 创建连接
    		Connection conn = factory.newConnection();
    		System.out.println("RabbitMqRecReview 获取到的链接是: " + conn);
    		// 创建通道
    		Channel channel = conn.createChannel();
    		System.out.println("RabbitMqRecReview 获取到的通道是: " + channel);
    		// 声明一个交换器
    		channel.exchangeDeclare(EXCHANGE_NAME, "direct");
    		// 绑定对应的队列
    		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "bindingKey");
    		// 声明消费回调函数
    		DeliverCallback deliverCallback = new DeliverCallback() {
    			@Override public void handle(String s, Delivery delivery) throws IOException {
    				System.out.println("RabbitMqRecReview receive: " + new String(delivery.getBody(), StandardCharsets.UTF_8));
    				// 确认(ack)接收到的消息
    				channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
    			}
    		};
    		channel.basicConsume(QUEUE_NAME, false, deliverCallback, new CancelCallback() {
    			@Override public void handle(String s) throws IOException {
    
    			}
    		});
    	}
    }
    

    Connection 和 Channel

    无论生产者和消费者都需要和 RabbitMQ Broker 建立连接,这个连接就是一条 TCP 连接,也就是 Connection。一旦 TCP 连接建立成功,紧接着需要创建一个 AMQP 的信道(Channel),每条信道都会被指派一个唯一的 ID。信道是建立在 Connection 之上的虚拟连接,RabbitMQ 处理的每条 AMQP 指令都是通过信道完成的。
    在这里插入图片描述
    在这里插入图片描述

    以上介绍了生产者(Producer ) 、消费者(Consumer) 、队列(Queue) 、交换器(Exchange)、路由键(RoutingKey )、绑定(Binding)、连接(Connection)和信道(Channel) 。

  • 相关阅读:
    Java入门
    Java入门
    字符串常用方法(转载--https://www.cnblogs.com/ABook/p/5527341.html)
    SSM-8FastDfs搭建
    SSM7-nginx的反向代理和负载均衡
    SSM-6nginx Linux下的安装
    SSM-5zookeeper在LINUX上自启
    SSM4-Linux上jdk、tomcat、zookeeper------tar zxvf的安装
    SSM3-SVN的安装和搭建环境
    SSM2-搭建maven常见的错误以及解决方法
  • 原文地址:https://www.cnblogs.com/liufeichn/p/11961622.html
Copyright © 2020-2023  润新知