无名交换机
前面都是发送消息到队列,交换机的参数设置为空串"",其实使用的是默认交换机。
channel.basicPublish("", queueName, null, message.getBytes());
可以看出默认交换机属于直接交换机类型direct。
默认交换机会路由到指定的的队列。
交换机与队列是通过routingkey进行绑定。之前使用无名交换机,这个routingkey默认就是队列的名称。
简单模式,工作模式流程图
发布订阅模式路程图
一条消息通过交换机给不同的队列,多个消费者就可以消费相同的消息。
所以,发布订阅模式就使用到了交换机。
交换机
rabbitmq消息传递模型的核心思想是:生产者生产的消息从不会直接发送给队列。通常生产者都不知道消息传递到了哪些队列。
生产者只是将消息发送给交换机。
交换机的工作内容
1.接收来自生产者的消息。
2.将它们推入队列。
交换机应该把这些消息放入特定队列,还是许多队列,还是丢失掉,是由交换机的类型决定的。
交换机的类型
1.直接类型direct
2.主题类型topic
3.标题类型headers(不常用)
4.扇出类型fanout
临时队列
是不带持久化的消息队列,一旦断开消费者的连接,队列会自动删除。
测试一旦生产者挂掉,临时队列也会删除。
每当连接到rabbitmq时,需要一个全新的空队列,为此创建一个具有随机名称的队列。或者让服务器为我们选择一个随机队列名称那就更好了。
创建临时队列的方法:
//创建临时队列 String queueName = channel.queueDeclare().getQueue();
绑定
binding就是exchange与queue之间的桥梁。
Fanout扇出(广播)
是将接收到的消息广播到它知道的所有队列中。
系统中默认的交换机
图中binding是一样的,就像qq群消息群发,大喇叭一样,发一条消息所有的消费者都能看见。
交换机logs
队列名随机产生
接收者1
package com.xkj.org.mq.fanout;
import com.rabbitmq.client.*;
import com.xkj.org.utils.RabbitMQUtil;
public class Receiver01 {
//交换机名称
public static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception {
Channel channel = RabbitMQUtil.getChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
//声明一个临时队列
//生成一个临时队列,队列名称是随机的
//当消费者断开与队列的连接的时候,队列就自动删除
String queueName = channel.queueDeclare().getQueue();
//绑定交换机与队列
channel.queueBind(queueName, EXCHANGE_NAME, "");
System.out.println("receiver01等待接收消息,把接收到的消息打印出来....");
DeliverCallback deliverCallback = (consumerTag, message) -> {
System.out.println("receiver01接收到的消息:"+ new String(message.getBody(), "UTF-8"));
};
CancelCallback cancelCallback = consumerTag -> {
System.out.println("receiver01消息消费被中断...");
};
channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
}
}
接收者2
package com.xkj.org.mq.fanout;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
import com.xkj.org.utils.RabbitMQUtil;
public class Receiver02 {
//交换机名称
public static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception {
Channel channel = RabbitMQUtil.getChannel();
//声明交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
//声明一个临时队列
//生成一个临时队列,队列名称是随机的
//当消费者断开与队列的连接的时候,队列就自动删除
String queueName = channel.queueDeclare().getQueue();
//绑定交换机与队列
channel.queueBind(queueName, EXCHANGE_NAME, "");
System.out.println("receiver02等待接收消息,把接收到的消息打印出来....");
DeliverCallback deliverCallback = (consumerTag, message) -> {
System.out.println("receiver02接收到的消息:"+ new String(message.getBody(), "UTF-8"));
};
CancelCallback cancelCallback = consumerTag -> {
System.out.println("receiver02消息消费被中断...");
};
channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
}
}
发送者
package com.xkj.org.mq.fanout;
import com.rabbitmq.client.Channel;
import com.xkj.org.utils.RabbitMQUtil;
import java.io.IOException;
import java.util.Scanner;
public class EmitLog {
//交换机名称
public static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws IOException {
Channel channel = RabbitMQUtil.getChannel();
//声明交换机(如果先启动了消费者,交换机就已经声明了,这里就不能重复声明了)
// channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()) {
String message = scanner.next();
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
System.out.println("生产者发送消息:"+ message);
}
}
}
结论:两个消息接收者收到的消息是一模一样的。这里的routingKey绑定的是空串,相当于没有绑定,都是一样的。