RabbitMQ入门介绍以及安装使用

点点圈 提交于 2020-01-30 00:14:28

一、RabbitMQ初识

RabbitMQ是一个实现了高级消息队列协议(AMQP的消息代理(也叫消息中间件),它接受并转发消息。它可以帮你处理一些逻辑的事务,从而进行解耦,比如用户注册落库之后,还需要发送邮件验证、需要发送新人红包等等事情,就可以交给中间件去做。也可以把它当成一个邮局:当你想邮寄信件的时候,你会把信件放在投递箱中,并确信邮递员最终会将信件送到收件人的手里。在这个例子中,RabbitMQ就相当与投递箱、邮局和邮递员。

1.1 AMQP协议

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

1.2 典型应用场景

  1. 跨系统的异步通信 ,异步、解耦、削峰。
  2. 应用内的同步变成异步 秒杀:自己发送给自己
  3. 基于Pub/Sub模型实现的事件驱动 ,摒弃ELT(比如全量 同步商户数据); 摒弃API(比如定时增量获取用户、获取产品,变成增量广播)。
  4. 利用RabbitMQ实现事务的最终一致性

1.3 RabbitMQ的特性

RabbitMQ使用Erlang语言编写,使用Mnesia数据库存储消息。

  1. 可靠性(Reliability) RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
  2. 灵活的路由(Flexible Routing) 在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在 一起,也通过插件机制实现自己的 Exchange 。
  3. 消息集群(Clustering) 多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker 。
  4. 高可用(Highly Available Queues) 队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下
    队列仍然可用。
  5. 多种协议(Multi-protocol) RabbitMQ 支持多种消息队列协议,比如 AMQP、STOMP、MQTT 等等。
  6. 多语言客户端(Many Clients) RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby、PHP、C#、 JavaScript 等等。
  7. 管理界面(Management UI) RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集群 中的节点。
  8. 插件机制(Plugin System) RabbitMQ提供了许多插件,以实现从多方面扩展,当然也可以编写自己的插件。

1.4 工作模型

在这里插入图片描述

  1. Broker
    我们要使用 RabbitMQ 来收发消息,必须要安装一个 RabbitMQ 的服务,可以安装在 Windows 上面也可以安装在 Linux 上面,默认是 5672 的端口。这台 RabbitMQ 的服务器我们把它叫做 Broker, MQ 服务器帮助我们做的事情就是存储、转发消息。
  2. Connection
    无论是生产者发送消息,还是消费者接收消息,都必须要跟 Broker 之间建立一个连接,这个连接是一个 TCP 的长连接。
  3. Channel
    如果所有的生产者发送消息和消费者接收消息,都直接创建和释放 TCP 长连接的话,对于 Broker 来说肯定会造成很大的性能损耗,因为 TCP 连接是非常宝贵的资源,创建和释放也要消耗时间。所以在 AMQP 里面引入了 Channel 的概念,它是一个虚拟的连接。我们把它翻译成通道,或者消息信道。这样我们就可以在保持的 TCP 长连接里面去创建和释放Channel,大大了减少了资源消耗。另外一个需要注意的是,Channel 是 RabbitMQ 原生 API 里面的最重要的编程接口,也就是说我们定义交换机、队列、绑定关系,发送消息消费消息,调用的都是 Channel 接口上的方法。
  4. Queue
    在其他一些 MQ 里面,比如ActiveMQ ,消息都是发送到队列上的。队列是真正用来存储消息的,是一个独立运行的进程,有自己的数据库。消费者获取消息有两种模式,一种是 Push 模式,只要生产者发到服务器,就马上推送给消费者。另一种是 Pull 模式,消息存放在服务端,只有消费者主动获取才能拿到消息。消费者需要写一个 while 循环不断地从队列获取消息吗?不需要,我们可以基于事件机制,实现消费者对队列的监听。
    由于队列有 FIFO 的特性,只有确定前一条消息被消费者接收之后,才会把这条消息从数据库删除,继续投递下一条消息。
  5. Exchange
    在 RabbitMQ 里面永远不会出现消息直接发送到队列的情况。因为在 AMQP 里面引入了交换机(Exchange)的概念,用来实现消息的灵活路由。
    交换机是一个绑定列表,用来查找匹配的绑定关系。队列使用绑定键(Binding Key)跟交换机建立绑定关系。生产者发送的消息需要携带路由键(Routing Key),交换机收到消息时会根据它保存的绑定列表,决定将消息路由到哪些与它绑定的队列上。注意:交换机与队列、队列与消费者都是多对多的关系。
  6. Vhost
    我们每个需要实现基于 RabbitMQ 的异步通信的系统,都需要在服务器上创建自己要用到的交换机、队列和它们的绑定关系。如果某个业务系统不想跟别人混用一个系统,怎么办?再采购一台硬件服务器单独安装一个 RabbitMQ 服务?这种方式成本太高了。在同一个硬件服务器上安装多个 RabbitMQ 的服务呢?比如再运行一个 5673 的端口?
    没有必要,因为 RabbitMQ 提供了虚拟主机 VHOST。VHOST 除了可以提高硬件资源的利用率之外,还可以实现资源的隔离和权限的控制。它的作用类似于编程语言中的 namespace 和 package,不同的 VHOST 中可以有同名的 Exchange 和 Queue,它们是完全透明的。
    这个时候,我们可以为不同的业务系统创建不同的用户(User),然后给这些用户分配 VHOST 的权限。比如给风控系统的用户分配风控系统的 VHOST 的权限,这个用户可以访问里面的交换机和队列。给超级管理员分配所有 VHOST 的权限。
  7. Producer生产者:主要将消息投递到对应的Exchange上面。一般是独立的程序。
  8. Consumer消费者:消息的接收者,一般是独立的程序。

1.5 三种主要的交换机

1.5.1 Direct Exchange 直连交换机

定义:直连类型的交换机与一个队列绑定时,需要指定一个明确的binding key。
路由规则:发送消息到直连类型的交换机时,只有routing key跟binding key完全匹配时,绑定的队列才能收到消息。
在这里插入图片描述

例如:

 // 只有队列1能收到消息
channel.basicPublish("MY_DIRECT_EXCHANGE", "key1", null, msg.getBytes());

1.5.2 Topic Exchange 主题交换机

定义:主题类型的交换机与一个队列绑定时,可以指定按模式匹配的routing key。
通配符有两个,* 代表匹配一个单词。# 代表匹配零个或者多个单词。单词与单词之间用 . 隔开。
路由规则:发送消息到主题类型的交换机时,routing key符合binding key的模式时,绑定的队列才能收到消息。
在这里插入图片描述
例如:

 // 只有队列1能收到消息
channel.basicPublish("MY_TOPIC_EXCHANGE", "ab.123", null, msg.getBytes());
// 队列2和队列3能收到消息
channel.basicPublish("MY_TOPIC_EXCHANGE", "bc.abc", null, msg.getBytes());

1.5.2 Fanout Exchange 广播交换机

定义:广播类型的交换机与一个队列绑定时,不需要指定binding key。
路由规则:当消息发送到广播类型的交换机时,不需要指定routing key,所有与之绑定的队列都能收到消息。
在这里插入图片描述
例如:

 // 3个队列都会收到消息
channel.basicPublish("MY_FANOUT_EXCHANGE", "", null, msg.getBytes());

二、安装运行

2.1 windows和linux系统

直接官网下载压缩包,解压即可,到sbin目录启动rabbitmq-server即可。

2.2 mac

mac系统下载安装包也可以,本人是用homebrew安装的。

brew install rabbitmq

安装后会有启动命令。

# 启动
$ brew services start rabbitmq

# 重启
$ brew services restart rabbitmq

# 停止
$ brew services stop rabbitmq

启动完成访问http://localhost:15672/#/。看到下面页面就是安装成功了。
在这里插入图片描述
账号密码都是guest。也不用输入。直接login就行。

三、简单的生产消费例子

先引入pom依赖。基于springboot的。

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

3.1 生产者

package com.example.demo;

/**
 * @author : pengweiwei
 * @date : 2020/1/29 7:09 下午
 */

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.util.Map;

public class MyProducer {
    private final static String QUEUE_NAME = "ORIGIN_QUEUE";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        // 连接IP
        factory.setHost("127.0.0.1");
        // 连接端口
        factory.setPort(5672);
        // 虚拟机
        factory.setVirtualHost("/");
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();

        String msg = "Hello, RabbitMQ";
        // 声明队列
        /**
         * 第一个参数:String queue
         * 第二个参数:boolean durable
         * 第三个参数:boolean exclusive,
         * 第四个参数:boolean autoDelete
         * 第五个参数:Map<String, Object> arguments
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 发送消息(发送到默认交换机AMQP Default,Direct) 如果有一个队列名称跟Routing Key相等,那么消息会路由到这个队列

        /**
         * 第一个参数:String exchange
         * 第二个参数:String routingKey
         * 第三个参数:BasicProperties props
         * 第四个参数:byte[] body
         */
        channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        channel.close();
        conn.close();
    }
}

3.2 消费者

package com.example.demo;

/**
 * @author : pengweiwei
 * @date : 2020/1/29 7:09 下午
 */

import com.rabbitmq.client.*;

import java.io.IOException;

public class MyConsumer {
    private final static String QUEUE_NAME = "ORIGIN_QUEUE";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory(); 
        // 连接IP
        factory.setHost("127.0.0.1");
        // 默认监听端口
        factory.setPort(5672);
        // 虚拟机
        factory.setVirtualHost("/");
        // 设置访问的用户
        factory.setUsername("guest");
        factory.setPassword("guest");
        // 建立连接
        Connection conn = factory.newConnection();
        // 创建消息通道
        Channel channel = conn.createChannel();
        /**
         * 声明队列
         * 第一个参数:String queue
         * 第二个参数:boolean durable
         * 第三个参数:boolean exclusive,
         * 第四个参数:boolean autoDelete
         * 第五个参数:Map<String, Object> arguments
         */
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println(" Waiting for message....");
        // 创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                String msg = new String(body, "UTF-8");
                System.out.println("Received message : '" + msg + "'");
            }
        };
        /**
         * 开始获取消息
         * 第一个参数:String queue
         * 第二个参数:boolean autoAck
         * 第三个参数:Consumer callback
         */
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

2.3 运行结果

在这里插入图片描述

2.4 参数说明

声明交换机的参数

String type:交换机的类型,direct, topic, fanout中的一种。
boolean durable:是否持久化,代表交换机在服务器重启后是否还存在。

声明队列的参数

boolean durable:是否持久化,代表队列在服务器重启后是否还存在。
boolean exclusive:是否排他性队列。排他性队列只能在声明它的Connection中使用,连接断开时自动删除。
boolean autoDelete:是否自动删除。如果为true,至少有一个消费者连接到这个队列,之后所有与这个队列连接 的消费者
都断开时,队列会自动删除。
Map<String, Object> arguments:队列的其他属性。含义详细见下表。
属性 含义
x-message-ttl 队列中消息的存活时间,单位毫秒
x-expires 队列在多久没有消费者访问以后会被删除
x-max-length 队列的最大消息数
x-max-length-bytes 队列的最大容量,单位 Byte
x-dead-letter-exchange 队列的死信交换机
x-dead-letter-routing-key 死信交换机的路由键
x-max-priority 队列中消息的最大优先级,消息的优先级不能超过它

消息属性 BasicProperties
主要的参数:

参数 释义
Map<String,Object> headers 消息的其他自定义参数
Integer deliveryMode 持久化,其他:瞬态
Integer priority 消息的优先级
String correlationId 关联 ID,方便 RPC 相应与请求关联
String replyTo 回调队列
String expiration TTL , 消息过期时间,单位毫秒
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!