1、RabbitMQ交换机的概念
RabbitMQ传递消息的核心思想:生产者生产的消息不会直接发送到队列,只能将消息发送到交换机exchange;
交换机的工作内容很简单,一方面接收生产者发过来的消息,另一方面推送到队列中;
交换机知道如何处理接收到的消息,是把这些消息放到特定的队列还是丢弃它们,就由交换机的类型决定。
1.1、交换机的类型
- 直接类型(direct)
- 扇出类型(fanout)
- 标题类型(headers)
- 主题类型(topic)
1.2、直接类型(direct)默认的exchange
/**
* 1.发送到那个交换机
* 2.队列名称
* 3.其他参数 消息持久化参数:MessageProperties.PERSISTENT_TEXT_PLAIN
* 4.要发送的消息体
*/
channel.basicPublish("",queueName,null,message.getBytes());
消息能路由发送到队列中其实是由 routingKey(bindingkey) 绑定 key 指定的
1.3、临时队列
每次连接到RabbitMQ时,需要一个全新的空队列,我们可以创建一个具有随机名称的队列,
或者让服务器帮我们创建一个,一旦断开消费者连接,队列将自动删除
String queueName=channel.queueDeclare.getQueue();
1.4、绑定(bindings)
binding就是exchange和queue之间的桥梁,声明了那个exchange和那个队列进行了绑定关系
2、Fanout类型交换机
Fanout这个类型的交换机,就是将所收到的消息广播到所有绑定的队列中
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
//Fanout交换机实例
public class Task5 {
private static final String EXCHANGE_NAME="fanouttest1";
public static void main(String[] args) throws Exception {
Channel channel= ChannelUnit.getChannel();
/**
* 1、exchange交换机的名称
* 2、exchange交换机的类型
*/
channel.exchangeDeclare(EXCHANGE_NAME,BuiltinExchangeType.FANOUT);
Scanner scanner=new Scanner(System.in);
System.out.println("请输入信息");
while(scanner.hasNext()){
String message=scanner.nextLine();
/**
* 1.交换机exchange的名称
* 2.队列名称
* 3.其它参数
* 4.要发送的消息
*/
channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes(StandardCharsets.UTF_8));
System.out.println("生产者发送消息:"+message);
}
}
}
import com.example.wwy.rabbit.units.ChannelUnit;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
//消费者
public class Work3 {
private static final String EXCHANGE_NAME="fanouttest1";
public static void main(String[] args) throws Exception {
Channel channel= ChannelUnit.getChannel();
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//产生一个临时队列
String queueName=channel.queueDeclare().getQueue();
/**
* 1、队列名称
* 2、交换机名称
* 3、routingKey 绑定的Key
*/
channel.queueBind(queueName,EXCHANGE_NAME,"");
System.out.println("W3等待接收消息。。。");
DeliverCallback deliverCallback=(var1,var2)->{
String message=new String(var2.getBody(),"UTF-8");
System.out.println("W3接收到的消息是:【"+message+"】");
};
CancelCallback cancelCallback=(var1)->{
System.out.println("消费消息被中断");
};
channel.basicConsume(queueName,true,deliverCallback,cancelCallback);
}
}
3、Direct类型交换机
exchange 的绑定类型是direct,根据绑定routingKey去给指定的队列发送消息;
但是它绑定的多个队列的 key 如果都相同,在这种情况下虽然绑定类型是 direct 但是它表现的就和 fanout 有点类似了,就跟广播差不多
import com.example.wwy.rabbit.units.ChannelUnit;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
//生产者
public class DirectTask1 {
private static final String DIRECT_NAME="direct_log";
public static void main(String[] args) throws Exception {
Channel channel = ChannelUnit.getChannel();
//生成一个交换机
channel.exchangeDeclare(DIRECT_NAME, BuiltinExchangeType.DIRECT);
//创建多个Bindingkey
Map<String ,String> map=new HashMap<>();
map.put("orange","信息orange");
map.put("black","信息black");
map.put("green","信息green");
for(Map.Entry<String,String> a:map.entrySet()){
String bindingkey=a.getKey();
String message=a.getValue();
channel.basicPublish(DIRECT_NAME,bindingkey,null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("生产者发送消息;【"+message+"】");
}
}
}
import com.example.wwy.rabbit.units.ChannelUnit;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
//消费者1
public class DirectWork1 {
private static final String DIRECT_NAME="direct_log";
public static void main(String[] args) throws Exception {
Channel channel = ChannelUnit.getChannel();
//设置交换机
channel.exchangeDeclare(DIRECT_NAME, BuiltinExchangeType.DIRECT);
String queueName="q1";
//设置队列
channel.queueDeclare(queueName,false,false,true,null);
//队列绑定
channel.queueBind(queueName,DIRECT_NAME,"orange");
System.out.println("DW1等待接收消息。。。");
DeliverCallback deliverCallback=(var1, var2)->{
String message=new String(var2.getBody(),"UTF-8");
System.out.println(message);
};
CancelCallback cancelCallback=(var1)->{
System.out.println("DW1消息接收失败");
};
channel.basicConsume(queueName,true,deliverCallback,cancelCallback);
}
}
import com.example.wwy.rabbit.units.ChannelUnit;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
//消费者2
public class DirectWork2 {
private static final String DIRECT_NAME="direct_log";
public static void main(String[] args) throws Exception {
Channel channel = ChannelUnit.getChannel();
//设置交换机
channel.exchangeDeclare(DIRECT_NAME, BuiltinExchangeType.DIRECT);
String queueName="q2";
//设置队列
channel.queueDeclare(queueName,false,false,true,null);
//队列绑定
channel.queueBind(queueName,DIRECT_NAME,"black");
channel.queueBind(queueName,DIRECT_NAME,"green");
System.out.println("DW2等待接收消息。。。");
DeliverCallback deliverCallback=(var1, var2)->{
String message=new String(var2.getBody(),"UTF-8");
System.out.println(message);
};
CancelCallback cancelCallback=(var1)->{
System.out.println("DW2消息接收失败");
};
channel.basicConsume(queueName,true,deliverCallback,cancelCallback);
}
}
4、Topics类型交换机
发送到类型是 topic 交换机的消息的 routing_key 不能随意写,必须满足一定的要求,它必须是一个单词列表,以点号分隔开。
这些单词可以是任意单词,比如说:“stock.usd.nyse”, “nyse.vmw”,“quick.orange.rabbit”.这种类型的。当然最多不能超过 255 个字节。
引入了两个替换符
*(星号)可以代替一个单词
#(井号)可以替代零个或多个单词
import com.example.wwy.rabbit.units.ChannelUnit;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
//生产者
public class TopicsTask1 {
private static final String DIRECT_NAME="topics_log";
public static void main(String[] args) throws Exception {
Channel channel = ChannelUnit.getChannel();
//生成一个交换机
channel.exchangeDeclare(DIRECT_NAME, BuiltinExchangeType.TOPIC);
//创建多个Bindingkey
Map<String ,String> map=new HashMap<>();
map.put("quick.orange.rabbit","被队列 Q1Q2 接收到");
map.put("lazy.orange.elephant","被队列 Q1Q2 接收到");
map.put("quick.orange.fox","被队列 Q1 接收到");
map.put("lazy.brown.fox","被队列 Q2 接收到");
map.put("lazy.pink.rabbit","虽然满足两个绑定但只被队列 Q2 接收一次");
map.put("quick.brown.fox","不匹配任何绑定不会被任何队列接收到会被丢弃");
map.put("quick.orange.male.rabbit","是四个单词不匹配任何绑定会被丢弃");
map.put("lazy.orange.male.rabbit","是四个单词但匹配 Q2");
for(Map.Entry<String,String> a:map.entrySet()){
String bindingkey=a.getKey();
String message=a.getValue();
channel.basicPublish(DIRECT_NAME,bindingkey,null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("生产者发送消息;【"+message+"】");
}
}
}
import com.example.wwy.rabbit.units.ChannelUnit;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
//消费者1
public class TopicsWork1 {
private static final String DIRECT_NAME="topics_log";
public static void main(String[] args) throws Exception {
Channel channel = ChannelUnit.getChannel();
//设置交换机
channel.exchangeDeclare(DIRECT_NAME, BuiltinExchangeType.TOPIC);
String queueName="q1";
//设置队列
channel.queueDeclare(queueName,false,false,true,null);
//队列绑定
channel.queueBind(queueName,DIRECT_NAME,"*.orange.*");
System.out.println("DW1等待接收消息。。。");
DeliverCallback deliverCallback=(var1, var2)->{
String message=new String(var2.getBody(),"UTF-8");
System.out.println(message);
};
CancelCallback cancelCallback=(var1)->{
System.out.println("DW1消息接收失败");
};
channel.basicConsume(queueName,true,deliverCallback,cancelCallback);
}
}
import com.example.wwy.rabbit.units.ChannelUnit;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
//消费者2
public class TopicsWork2 {
private static final String DIRECT_NAME="topics_log";
public static void main(String[] args) throws Exception {
Channel channel = ChannelUnit.getChannel();
//设置交换机
channel.exchangeDeclare(DIRECT_NAME, BuiltinExchangeType.TOPIC);
String queueName="q2";
//设置队列
channel.queueDeclare(queueName,false,false,true,null);
//队列绑定
channel.queueBind(queueName,DIRECT_NAME,"*.*.rabbit");
channel.queueBind(queueName,DIRECT_NAME,"lazy.#");
System.out.println("DW2等待接收消息。。。");
DeliverCallback deliverCallback=(var1, var2)->{
String message=new String(var2.getBody(),"UTF-8");
System.out.println(message);
};
CancelCallback cancelCallback=(var1)->{
System.out.println("DW2消息接收失败");
};
channel.basicConsume(queueName,true,deliverCallback,cancelCallback);
}
}
小结,交换机主要是根据BindingKey去给对应的队列传输消息。
- Fanout 广播类型:无需bindingkey,只要与交换机绑定的队列都会收到消息
- Direct 直接类型:根据具体的bindkey,交换机给绑定的队列发送消息
- Topic 主题类型:bindkey类似于模拟查询。