文章目录

Exchange

描述

用于接收消息,并根据路由键转发消息到所绑定的队列

结构图

RabbitMQ学习——常见概念详解_虚拟主机

蓝色框表示发送消息,然后消息通过路由关系路由到Queue1或Queue2

绿色框表示接收消息,消费者与队列建立监听,去消费消息

黄色框表示路由键绑定关系

交换机属性

  • Name:交换机名称
  • Type:交换机类型direct、topic、fanout、headers
  • Durablity:是否需要持久化 true|false
  • Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange
  • Internal:当前Exchange是否用于RabbitMQ内部使用,默认为false
  • Arguments:扩展参数,用于扩展AMQP协议自制定使用

Direct Exchange

  • 所有发送到Direct Exchange的消息被转发到RoutingKey中指定的Queue

RabbitMQ学习——常见概念详解_虚拟主机_02

如图所示,路由到队列名为Key的队列中去了

生产者代码:

public class Producer4DirectExchange {
public static void main(String[] args) throws IOException {
Connection connection = ConnectionUtil.getConn();

Channel channel = connection.createChannel();

//声明
String exchangeName = "test_direct_exchange";
String routingKey = "test.direct";

//发送
String msg = "Hello, I am Producer4DirectExchange";
channel.basicPublish(exchangeName,routingKey,null,msg.getBytes());
}
}

消费者代码:

public static void main(String[] args) throws IOException, InterruptedException {
Connection connection = ConnectionUtil.getConn();

Channel channel = connection.createChannel();
//声明
String exchangeName = "test_direct_exchange";
String exchangeType = "direct";
String queueName = "test_direct_queue";
String routingKey = "test.direct";

//声明Exchange
channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);

//声明一个队列
channel.queueDeclare(queueName,false,false,false,null);

//建立一个绑定关系
channel.queueBind(queueName,exchangeName,routingKey);

//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称,是否自动ACK,Consumer
channel.basicConsume(queueName,true,consumer);
while (true) {
//阻塞获取消息
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:"+msg);
}


}

公共类:

public class ConnectionUtil {
public static final String MQ_HOST = "192.168.222.101";
public static final String MQ_VHOST = "/";
public static final int MQ_PORT = 5672;

public static Connection getConn() {
//1. 创建一个ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(MQ_HOST);//配置host
connectionFactory.setPort(MQ_PORT);//配置port
connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost

//2. 通过连接工厂创建连接
try {
return connectionFactory.newConnection();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

登录控制台,可以看到名为​​test_direct_exchange​​​的交换机通过路由键​​test.direct​​​绑定到​​test_direct_queue​

RabbitMQ学习——常见概念详解_项目代码_03

Topic Exchange

  • 所有发送到Topic Exchange的消息都被转发到所有关心RoutingKey中指定Topic的队列上
  • Exchange将RoutingKey和某Topic进行模糊匹配,此时队列需要绑定一个Topic

可以使用通配符进行模糊匹配:
​​​#​​​ 匹配一个或多个词(单词的意思,不是一个字符)
​​​*​​​匹配一个词
​​​log.#​​​ 能匹配到"log.info.a"
​​​log.*​​ 只匹配"log.error"

RabbitMQ学习——常见概念详解_持久化_04

如上图,比如,user.news和user.weather能路由到第一个队列

生成者代码:

public class Producer4TopicExchange {
public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConn();

Channel channel = connection.createChannel();

//声明
String exchangeName = "test_topic_exchange";
String routingKey1 = "user.save";
String routingKey2 = "user.update";
String routingKey3 = "user.find.abc";


//发送
String msg = "Hello, I am Producer4TopicExchange";
channel.basicPublish(exchangeName,routingKey1,null,msg.getBytes());
channel.basicPublish(exchangeName,routingKey2,null,msg.getBytes());
channel.basicPublish(exchangeName,routingKey3,null,msg.getBytes());

CloseTool.closeElegantly(channel,connection);
}
}

消费者代码:

public class Consumer4TopicExchange {
public static void main(String[] args) throws IOException, InterruptedException {
Connection connection = ConnectionUtil.getConn();

Channel channel = connection.createChannel();
//声明
String exchangeName = "test_topic_exchange";
String exchangeType = "topic";
String queueName = "test_topic_queue";
String routingKey = "user.#";

//声明Exchange
channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);

//声明一个队列
channel.queueDeclare(queueName,false,false,false,null);

//建立一个绑定关系
channel.queueBind(queueName,exchangeName,routingKey);

//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称,是否自动ACK,Consumer
channel.basicConsume(queueName,true,consumer);
while (true) {
//阻塞获取消息
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:"+msg);
}

}
}

先启动消费者,然后发现下面两个绑定关系:

RabbitMQ学习——常见概念详解_虚拟主机_05

队列

RabbitMQ学习——常见概念详解_项目代码_06

再启动生产者,从消费者端可以看到如下输出:

收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange

说明3条消息都收到了,接下来,我们改一下消费者的路由键,改为:​​String routingKey = "user.*";​

再次启动

收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange

奇怪,怎么还是收到3条,带着疑问我们去看控制台

RabbitMQ学习——常见概念详解_项目代码_07


可以看到,之前的路由规则绑定还在。因此可以解释为啥能收到3条。

点击unbind解绑"user.#"

然后继续操作一把,查看输出

收到消息:Hello, I am Producer4TopicExchange
收到消息:Hello, I am Producer4TopicExchange

此时,只收到两条了。

Fanout Exchange

  • 不处理路由键,只需要简单的将队列绑定到交换机上
  • 发送到交换机的消息都会被转发到与该交互机绑定的所有队列上
  • 转发消息是最快的

RabbitMQ学习——常见概念详解_虚拟主机_08

消息不走路由键,只要队列与交换机有绑定关系就能收到。

生产者:

public class Producer4FanoutExchange {
public static void main(String[] args) throws IOException {
Connection connection = ConnectionUtil.getConn();

Channel channel = connection.createChannel();

//声明
String exchangeName = "test_fanout_exchange";
String routingKey = "nothing";

//发送
String msg = "Hello, I am Producer4FanoutExchange";
channel.basicPublish(exchangeName,routingKey,null,msg.getBytes());

CloseTool.closeElegantly(channel,connection);
}
}

消费者:

public class Consumer4FanoutExchange {
public static void main(String[] args) throws IOException, InterruptedException {
Connection connection = ConnectionUtil.getConn();

Channel channel = connection.createChannel();
//声明
String exchangeName = "test_fanout_exchange";
String exchangeType = "fanout";
String queueName = "test_fanout_queue";
String routingKey = "";//不设置路由键

//声明Exchange
channel.exchangeDeclare(exchangeName,exchangeType,true,false,false,null);
//声明一个队列
channel.queueDeclare(queueName,false,false,false,null);
//建立一个绑定关系
channel.queueBind(queueName,exchangeName,routingKey);

//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称,是否自动ACK,Consumer
channel.basicConsume(queueName,true,consumer);
while (true) {
//阻塞获取消息
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:"+msg);
}



}
}

RabbitMQ学习——常见概念详解_虚拟主机_09

RabbitMQ学习——常见概念详解_虚拟主机_10

Binding

  • Exchange和Exchange、Queue之间的连接关系
  • Binding中可以包含RoutingKey或参数

Queue

  • 消息队列,实际存储消息

属性

  • Durability:是否持久化
  • Auto delete: 若为yes,代表当最后一个监听被移除后,该Queue会自动被删除

Message

  • 服务器和应用程序之间传递的数据
  • 本质就是一段数据,由Properties和Payload(Body)组成

属性

  • delivery mode
  • headers(自定义属性放到这里面)
  • content_type
  • content_encoding
  • priority
  • correlation_id
  • reply_to:指令消息失败返回的队列
  • expiration:过期时间
  • message_id:消息ID

Producer:

public class Producer {
public static final String MQ_HOST = "192.168.222.101";
public static final String MQ_VHOST = "/";
public static final int MQ_PORT = 5672;

public static void main(String[] args) throws IOException, TimeoutException {
//1. 创建一个ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(MQ_HOST);//配置host
connectionFactory.setPort(MQ_PORT);//配置port
connectionFactory.setVirtualHost(MQ_VHOST);//配置vHost

//2. 通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3. 通过connection创建一个Channel
Channel channel = connection.createChannel();

Map<String,Object> headers = new HashMap<>();
headers.put("var1","abc");
headers.put("var2","sdd");
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2) //2:持久化投递;1:非持久化(未消费的消息重启后就没了)
.contentEncoding("UTF-8")
.expiration("5000")//5s
.headers(headers)
.build();

//4. 通过Channel发送数据
for (int i = 0; i < 10; i++) {
String message = "Hello" + i;
//exchange为"",则通过routingKey取寻找队列
channel.basicPublish("","testQueue",properties,message.getBytes());
}

//5. 关闭连接
channel.close();
connection.close();

}
}

consumer:

public class Consumer {


public static final String QUEUE_NAME = "testQueue";

public static void main(String[] args) throws Exception {
Connection connection = ConnectionUtil.getConn();

//3. 通过connection创建一个Channel
Channel channel = connection.createChannel();

//4. 声明(创建)一个队列
channel.queueDeclare(QUEUE_NAME,true,false,false,null);

//5. 创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);

//6. 设置Channel
channel.basicConsume(QUEUE_NAME,true,queueingConsumer);

int num = 0;
//7. 获取消息
while (true) {
//nextDelivery 会阻塞直到有消息过来
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("收到:" + message);
Map<String,Object > headers = delivery.getProperties().getHeaders();
System.out.println("headers get va1 :" + headers.get("var1"));
}


}
}

Virtual host - 虚拟主机

  • 虚拟地址,用于进行逻辑隔离,最上层的消息路由
  • 一个Virtuall Host里面可以有若干个Exchange和Queue
  • 同一个Virtual Host里面不能有相同名称的Exchange或Queue

项目代码

​下载地址​