RabbitMQ基础篇

878 阅读11分钟

RabbitMQ基础篇

一、RabbitMQ介绍

RabbitMQ消息队列是一个开源的AMQP实现,服务器端使用Erlang语言编写的,支持多种客户端,如:Python、Ruby、.NET、Java、C,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面都表现不错,与SpringAMQP完美的整合,API丰富易用。

核心概念图解

image.png

核心概念

  • Producer生产者:创建消息(Message),然后发布到RabbitMQ中。
  • Consumer消费者:消息队列里面的消息。
  • Broker:RabbitMQ的服务端程序,可以认为一个MQ节点就是一个Broker。
  • Message消息:消息,包含了消息头、消息体,也包括多个属性,比如RoutingnKey(路由键)。
  • Queue队列:是RabbitMQ内部的对象,用于存储消息,消息只能存储在队列中
  • Channel信道:一条支持多路复用的通道,独立的双向数据流通道,可以发布、订阅、接收消息,信道是建立在真实的TCP连接内部的虚拟连接,复用TCP连接的通道。
  • Exchange交换机:生产者将消息发送到交换机,交换机将消息路由到一个或多个队列中,交换机有多个类型,队列和交换机是多对多的关系。
  • RoutingKey路由键:生产者将消息发送给交换机的时候,一般都会指定一个RoutingKey。用来指定这个消息的路由规则。
  • Binding:交换机与队列绑定起来的时候,一般会指定一个邦定键(BindingKey),这样RabbitMQ就知道如何正确地将消息路由到队列了。
  • Virtual Host虚拟主机:用于不同业务模块的逻辑隔离,一个Virtual Host里面可以有若干个Exchange和Queue,同一个Virtual Host里面不能有相同名称的Exchange和Queue。Virtual Host起到了环境隔离的作用,默认是/。

二、RabbitMQ安装

这里只介绍一个Docker安装RabbitMQ的流程(安装RabbitMQ单机版本,安装RabbitMQ集群后续在梳理)。Docker如何安装使用自行找篇文章学习一下,后续也会梳理Docker知识。

Docker 安装

  1. 拉取镜像docker pull rabbitmq:management
  2. 启动RabbbitMQ
docker run -d --hostname rabbit_host --name my_rabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=password -p 15672:15672 -p 5672:5672 rabbitmq:management
hostname:设定容器的主机名,它会被写到容器内的/etc/hostname 和 /etc/hosts,作为容器主机IP的别名,并且将显示在容器的bash中
-d 以守护进程⽅式在后台运⾏
-p 15672:15672 management 界⾯管理访问端⼝
-p 5672:5672 amqp 访问端⼝
--name:指定容器名
-e 参数
RABBITMQ_DEFAULT_USER ⽤户名
RABBITMQ_DEFAULT_PASS 密码
#主要端口介绍
4369 erlang 发现⼝
5672 client 端通信⼝
15672 管理界⾯ ui 端⼝
25672 server 间内部通信⼝
访问管理界面 ip:15672
  1. 进入到管理界面登录 image.png

image.png

三、Java整合RabbitMQ

这里介绍一个Java如何整合RabbitMQ

创建Maven工程

打开IDEA -> File -> New -> Project

image.png

选择maven工程,点击next

image.png

输入你们自己创建的工程名称,以及工程的maven坐标

image.png

修改pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.melo</groupId>
    <artifactId>rabbitmq-demo</artifactId>
    <version>1.0.0</version>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.10.0</version>
        </dependency>
    </dependencies>
</project>

四、RabbitMQ工作模式

  • 简单模式
  • work queues
  • Publish/Subscribe发布与订阅模式
  • Routing路由模式
  • Topic模式
  • RPC远程调用模式(这个一般不怎么用)

Hello World(RabbitMQ 入门案例---简单模式)

一个生产者、一个消费者,不用指定交换机,使用默认交换机(默认交换机类型就是Direct)。

image.png

  • 生产者代码
package com.melo.rabbit.mq.demo.simple;

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

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * 消息发送者
 *
 * @author melo
 * @date 2021/9/12 16:32
 */
public class Send {


    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        try (
                //JDK7语法自动关闭资源
                //创建连接
                Connection connection = connectionFactory.newConnection();
                //创建信道
                Channel channel = connection.createChannel()) {
            /*
             *  创建一个队列
             *  第一个参数:队列名称
             *  第二个参数:持久化配置,true为持久化,mq重启后还在
             *  第三个参数:是否独占,只能有一个消费者监听队列。
             *  第四个参数:是否自动删除
             *  第五个参数:其他参数
             *
             * **队列不存在则会自动创建,如果存在则不会覆盖。
             */
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            String message = "Hello World";
            /*
             * 发送消息
             */
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
            System.out.println("send message " + message);
        }
    }
}


  • 消费者代码
package com.melo.rabbit.mq.demo.simple;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive {

    private static final String QUEUE_NAME = "hello";


    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        System.out.println("======开始监听======");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumerTag:" + consumerTag);
                System.out.println("envelope:" + envelope);
                System.out.println("properties:" + properties);
                System.out.println("body:" + new String(body, StandardCharsets.UTF_8));
            }
        };
        channel.basicConsume(QUEUE_NAME, true, consumer);
    }
}

工作队列---轮询策略

一个生产者、多个消费者,有轮询和公平策略,不用指定交换机,使用默认交换机。

消费者代码中有一行代码 channel.basicQos(1); 这行代码的意思就是限制消费者每次只能消费一条消息,消费完了才能消费下一条消息。如果不加这行代码,就是轮询策略,假设生产者这发送10条消息到消息队列中,则每个消费者各消费5条消息。如果加上这行代码,就是公平策略,则每个消费者按照自己的能力消费消息,可能是消费者1消费了7条消息,消费者2消费3条消息。

image.png

  • 生产者代码
package com.melo.rabbit.mq.demo.work.fair;

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

import java.nio.charset.StandardCharsets;

/**
 * 消息发送者
 *
 * @author melo
 * @date 2021/9/12 16:32
 */
public class Send {


    private static final String QUEUE_NAME = "work_queue_random_ribbon";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        try (
                //JDK7语法自动关闭资源
                //创建连接
                Connection connection = connectionFactory.newConnection();
                //创建信道
                Channel channel = connection.createChannel()) {
            /*
             *  创建一个队列
             *  第一个参数:队列名称
             *  第二个参数:持久化配置,true为持久化,mq重启后还在
             *  第三个参数:是否独占,只能有一个消费者监听队列。
             *  第四个参数:是否自动删除
             *  第五个参数:其他参数
             *
             * **队列不存在则会自动创建,如果存在则不会覆盖。
             */
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            for (int i = 0; i < 10; i++) {
                String message = "天王盖地虎,宝塔镇河妖" + i;
                /*
                 * 发送消息
                 */
                channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
                System.out.println("send message " + message);
            }
        }
    }
}
  • 消费者1代码
package com.melo.rabbit.mq.demo.work.fair;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive {

    private static final String QUEUE_NAME = "work_queue_random_ribbon";


    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //限制消费者每次只能消费一条消息,处理完成才可以处理下一条
        channel.basicQos(1);
        System.out.println("======开始监听======");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("body:" + new String(body, StandardCharsets.UTF_8));
                //手动确认ACK,不是批量的
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}
  • 消费者2代码
package com.melo.rabbit.mq.demo.work.fair;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive2 {

    private static final String QUEUE_NAME = "work_queue_random_ribbon";


    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("melo");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //限制消费者每次只能消费一条消息,处理完成才可以处理下一条
        channel.basicQos(1);
        System.out.println("======开始监听======");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("body:" + new String(body, StandardCharsets.UTF_8));
                //手动确认ACK,不是批量的
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(QUEUE_NAME, false, consumer);
    }
}

Publish/Subscribe发布与订阅模式

发布订阅模型中,消息生产者不再是直接面向queue(队列名称),而是面向exchange,都需要经过exchange来进行消息的发送,所有往同一个Fanout交换机的消息都会被所有监听这个交换机的消费者收到。

要学习发布订阅模式,那么就要先了解一个RabbitMQ的交换机

RabbitMQ交换机介绍

生产者将消息发送到交换机(exchange),交换机将消息路由到一个或者多个队列中,交换机有多种类型,队列和交换机是多对多的关系。交换机只负责转发消息,不具备存储消息的能力,如果没有队列和交换机绑定,或者没有符合的路由规则,则消息会被丢失。

RabbitMQ交换机的种类

  • Fanout Exchange
  • Direct Exchange
  • Topic Exchange
  • Header Exchange(基本不用)

Fanout Exchange(广播)

  • 只需要简单地将队列绑定到交换机上,一个发送到交换机的消息会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机获得了一份复制的消息。
  • Fanout交换机转发的消息是最快的,用于发布订阅、广播形式。
  • Fanout类型交换机,通过交换机和队列绑定,不用指定绑定的路由键,生产者发送消息到交换机,fanout交换机直接进行转发,消息不用指定RoutingKey。

image.png

生产者代码
package com.melo.rabbit.mq.demo.pub;

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

import java.nio.charset.StandardCharsets;

/**
 * 消息发送者
 *
 * @author melo
 * @date 2021/9/12 16:32
 */
public class Send {

    // 交换机名称
    private static final String EXCHANGE_NAME = "exchange_fanout";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        try (
                //JDK7语法自动关闭资源
                //创建连接
                Connection connection = connectionFactory.newConnection();
                //创建信道
                Channel channel = connection.createChannel()) {

            //绑定交换机,fanout扇形,即广播
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
            String message = "Hello World";
            //此处不用设置队列名称
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes(StandardCharsets.UTF_8));
            System.out.println("消息【"+message+"】发送成功");
        }
    }
}


消费者代码(3个消费者代码相同)
package com.melo.rabbit.mq.demo.pub;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive {

    private static final String EXCHANGE_NAME = "exchange_fanout";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //绑定交换机 声明交换机类型为Fanout
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        //获取队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机 fanout交换机不用指定RoutingKey
        channel.queueBind(queue, EXCHANGE_NAME, "");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("body:" + new String(body, StandardCharsets.UTF_8));
                //手动确认ACK,不是批量的
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queue, false, consumer);
    }
}


Direct Exchange(路由模式)

  • 将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。
  • 例子:如果一个队列绑定到该交换机上路由键为"aabb",则只有被标记为"aabb"的消息才会被转发。不会转发"aabb.cc",需要完全匹配"aabb"。
  • Direct类型交换机,通过交换机和队列绑定,绑定指定的RoutingKey,生产者发送消息到交换机,交换机根据消息的RoutingKey进行转发到对应的队列,消息需要指定RoutingKey。

image.png

生产者代码
package com.melo.rabbit.mq.demo.direct;

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

import java.nio.charset.StandardCharsets;

/**
 * 消息发送者
 *
 * @author melo
 * @date 2021/9/12 16:32
 */
public class Send {


    private static final String EXCHANGE_NAME = "exchange_direct";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        try (
                //JDK7语法自动关闭资源
                //创建连接
                Connection connection = connectionFactory.newConnection();
                //创建信道
                Channel channel = connection.createChannel()) {

            //绑定交换机,direct 直连交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
            String errorMessage = "订单系统的错误日志";
            String infoMessage = "订单系统的信息日志";
            String debugMessage = "订单系统的debug日志";
            String warnMessage = "订单系统的警告日志";

            channel.basicPublish(EXCHANGE_NAME, "errorRoutingKey", null, errorMessage.getBytes(StandardCharsets.UTF_8));
            channel.basicPublish(EXCHANGE_NAME, "infoRoutingKey", null, infoMessage.getBytes(StandardCharsets.UTF_8));
            channel.basicPublish(EXCHANGE_NAME, "debugRoutingKey", null, debugMessage.getBytes(StandardCharsets.UTF_8));
            channel.basicPublish(EXCHANGE_NAME, "infoRoutingKey", null, warnMessage.getBytes(StandardCharsets.UTF_8));
            System.out.println("消息【"+errorMessage+"】发送成功");
            System.out.println("消息【"+infoMessage+"】发送成功");
            System.out.println("消息【"+debugMessage+"】发送成功");
            System.out.println("消息【"+warnMessage+"】发送成功");
        }
    }
}

消费者代码(接收全部消息的消费者)
package com.melo.rabbit.mq.demo.direct;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive {

    private static final String EXCHANGE_NAME = "exchange_direct";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //绑定交换机 直连交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //获取队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机和RoutingKey
        channel.queueBind(queue, EXCHANGE_NAME, "warnRoutingKey");
        channel.queueBind(queue, EXCHANGE_NAME, "infoRoutingKey");
        channel.queueBind(queue, EXCHANGE_NAME, "debugRoutingKey");
        channel.queueBind(queue, EXCHANGE_NAME, "errorRoutingKey");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("第一个队列 body:" + new String(body, StandardCharsets.UTF_8));
                //手动确认ACK,不是批量的
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queue, false, consumer);
    }
}
消费者(只接收error消息)
package com.melo.rabbit.mq.demo.direct;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive1 {

    private static final String EXCHANGE_NAME = "exchange_direct";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //绑定交换机 直连交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //获取队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机和RoutingKey
        channel.queueBind(queue, EXCHANGE_NAME, "errorRoutingKey");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("第二个队列 body:" + new String(body, StandardCharsets.UTF_8));
                //手动确认ACK,不是批量的
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queue, false, consumer);
    }
}

Topic Exchange(通配符模式)

  • 主题交换机是一种发布/订阅模式,结合了路由交换机与扇形交换机的特点。
  • 将路由键和某模式进行匹配,此时队列需要绑定到一个模式上。
  • 符号 # 匹配一个或者多个词,符号 * 匹配一个词。
  • 例子:"abc.#"能匹配到"abc.def.ghi",但是"abc.* " 只能匹配到"abc.def"。
  • Topic交换机,通过交换机和队列绑定,指定绑定的通配符RoutingKey,生产者发送消息到交换机,交换机根据消息的路由key进行转发到对应的队列,消息要指定RoutingKey。

image.png

生产者代码

package com.melo.rabbit.mq.demo.topic;

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

import java.nio.charset.StandardCharsets;

/**
 * 消息发送者
 *
 * @author melo
 * @date 2021/9/12 16:32
 */
public class Send {


    private static final String EXCHANGE_NAME = "exchange_topic";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        try (
                //JDK7语法自动关闭资源
                //创建连接
                Connection connection = connectionFactory.newConnection();
                //创建信道
                Channel channel = connection.createChannel()) {

            //绑定交换机,direct 直连交换机
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
            String errorMessage = "订单系统的错误日志";
            String infoMessage = "订单系统的信息日志";
            String debugMessage = "订单系统的debug日志";
            String warnMessage = "商品系统的警告日志";

            channel.basicPublish(EXCHANGE_NAME, "order.log.error", null, errorMessage.getBytes(StandardCharsets.UTF_8));
            channel.basicPublish(EXCHANGE_NAME, "order.log.info", null, infoMessage.getBytes(StandardCharsets.UTF_8));
            channel.basicPublish(EXCHANGE_NAME, "order.log.debug", null, debugMessage.getBytes(StandardCharsets.UTF_8));
            channel.basicPublish(EXCHANGE_NAME, "product.log.warn", null, warnMessage.getBytes(StandardCharsets.UTF_8));
            System.out.println("消息【"+errorMessage+"】发送成功");
            System.out.println("消息【"+infoMessage+"】发送成功");
            System.out.println("消息【"+debugMessage+"】发送成功");
            System.out.println("消息【"+warnMessage+"】发送成功");
        }
    }
}


消费者1代码
package com.melo.rabbit.mq.demo.topic;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive {

    private static final String EXCHANGE_NAME = "exchange_topic";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //绑定交换机 直连交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //获取队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机和RoutingKey
        channel.queueBind(queue, EXCHANGE_NAME, "order.log.error");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("第一个队列 body:" + new String(body, StandardCharsets.UTF_8));
                //手动确认ACK,不是批量的
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queue, false, consumer);
    }
}

消费者2代码
package com.melo.rabbit.mq.demo.topic;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 消息接收
 *
 * @author melo
 * @date 2021/9/12 16:49
 */
public class Receive1 {

    private static final String EXCHANGE_NAME = "exchange_topic";

    public static void main(String[] args) throws Exception {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.137.124");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("password");
        connectionFactory.setVirtualHost("/dev");
        connectionFactory.setPort(5672);
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //绑定交换机 直连交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //获取队列
        String queue = channel.queueDeclare().getQueue();
        //绑定队列和交换机和RoutingKey
        channel.queueBind(queue, EXCHANGE_NAME, "*.log.*");

        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("第一个队列 body:" + new String(body, StandardCharsets.UTF_8));
                //手动确认ACK,不是批量的
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queue, false, consumer);
    }
}

Header Exchange(少用)

  • 根据发送的消息内容中的headers属性进⾏匹配, 在绑定Queue与Exchange时指定⼀组键值对。
  • 当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进⾏匹配。
  • 如果完全匹配则消息会路由到该队列,否则不会路由到该队列。
  • 不处理路由键。