RabbitMQ基础篇
一、RabbitMQ介绍
RabbitMQ消息队列是一个开源的AMQP实现,服务器端使用Erlang语言编写的,支持多种客户端,如:Python、Ruby、.NET、Java、C,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面都表现不错,与SpringAMQP完美的整合,API丰富易用。
核心概念图解
核心概念
- 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 安装
- 拉取镜像
docker pull rabbitmq:management - 启动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
- 进入到管理界面登录
三、Java整合RabbitMQ
这里介绍一个Java如何整合RabbitMQ
创建Maven工程
打开IDEA -> File -> New -> Project
选择maven工程,点击next
输入你们自己创建的工程名称,以及工程的maven坐标
修改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)。
- 生产者代码
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条消息。
- 生产者代码
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。
生产者代码
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。
生产者代码
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。
生产者代码
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绑定时指定的键值对进⾏匹配。
- 如果完全匹配则消息会路由到该队列,否则不会路由到该队列。
- 不处理路由键。