Fanout这种类型非常简单。正如从名称中猜到的那样,它是将接收到的所有消息广播到它知道的
所有队列中 只要队列和该模式的交换机有绑定关系,该队列都会收到消息 系统中默认有些exchange类型
书写fanout生产者代码
public class FanoutProducter {
//交换机的名称
public static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
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);
}
}
}
书写两个消费者
public class Consumer1 {
//交换机的名字
public static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
//声明一个交换机
/*
* 1 交换机名称
* 2 交换机模式
* */
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//声明一个队列 临时队列
/*
* 生成一个临时队列 队列的名称是随机的
* 当消费者断开与队列的连接的时候 队列就会自动删除
* */
String queueName= channel.queueDeclare().getQueue();
//绑定交换机与队列
/*
* 1 队列名称
* 2 交换机名称
* 3 路由key
* */
channel.queueBind(queueName,EXCHANGE_NAME,"");
System.out.println("等待接收消息 把接收到的消息打印在屏幕上..");
//接收消息
DeliverCallback deliverCallback =(consumerTag,message)->{
System.out.println("消费者1控制台打印接收到的消息:"+new String(message.getBody(),"UTF-8"));
};
//消费者取消消息时回调接口
channel.basicConsume(queueName,true,deliverCallback,consumerTag ->{});
}
}
public class Consumer2 {
//交换机的名字
public static final String EXCHANGE_NAME = "logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
//声明一个交换机
/*
* 1 交换机名称
* 2 交换机模式
* */
channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//声明一个队列 临时队列
/*
* 生成一个临时队列 队列的名称是随机的
* 当消费者断开与队列的连接的时候 队列就会自动删除
* */
String queueName= channel.queueDeclare().getQueue();
//绑定交换机与队列
/*
* 1 队列名称
* 2 交换机名称
* 3 路由key
* */
channel.queueBind(queueName,EXCHANGE_NAME,"");
System.out.println("等待接收消息 把接收到的消息打印在屏幕上..");
//接收消息
DeliverCallback deliverCallback =(consumerTag,message)->{
System.out.println("消费者2控制台打印接收到的消息:"+new String(message.getBody(),"UTF-8"));
};
//消费者取消消息时回调接口
channel.basicConsume(queueName,true,deliverCallback,consumerTag ->{});
}
}
启动生产者和消费者,并由生产者发送消息 观察两个消费者接收消息的情况
生产者:
消费者1:
消费者2:
bindings,绑定是交换机和队列之间的桥梁关系。也可以这么理解: 队列只对它绑定的交换机的消息感兴趣。绑定用参数: routingKey来表示也可称该参数为binding key,创建绑定我们用代码:channel.queueBind(queueName, EXCHANGE_ NAME, “routingKey”);绑定之后的意义由交换类型决定.
如果我们希望将日志消息写入磁盘的程序仅接收我们想要让指定队列接收的日志消息,也就是我们需要指定哪一个队列去接收我们交换机所发送的消息
Fanout 这种交换类型并不能给我们带来很大的灵活性它只能进行无意识的广播,在这里我们将使用direct这种类型来进行替换,这种类型的工作方式是,消息只去到它绑定的routingKey队列中去。
书写direct模式生产者代码:
public class DirectProducter {
//交换机的名称
public static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要发送给队列的路由key:");
while (scanner.hasNext()){
String routingkey = scanner.next();
System.out.println("请输入要发送的消息:");
String message = scanner.next();
channel.basicPublish(EXCHANGE_NAME,routingkey,null,message.getBytes("UTF-8"));
System.out.println("生产者发出消息:"+message);
System.out.println("请输入要发送给队列的路由key:");
}
}
}
书写direct消费者代码
direct消费者1,direct消费者1绑定了两个routingkey,info和error
public class DirectConsumer1 {
//交换机的名称
public static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
//声明一个交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//声明一个队列
channel.queueDeclare("console",false,false,false,null);
//绑定交换机 1队列名称 2交换机名称 3路由key
channel.queueBind("console",EXCHANGE_NAME,"info");
channel.queueBind("console",EXCHANGE_NAME,"waring");
//接收消息
DeliverCallback deliverCallback =(consumerTag, message)->{
System.out.println("Direct消费者1控制台打印接收到的消息:"+new String(message.getBody(),"UTF-8"));
};
//消费者取消消息时回调接口
channel.basicConsume("console",true,deliverCallback,consumerTag ->{});
}
}
direct消费者2,消费者2绑定一个routingkey, waring:
public class DirectConsumer2 {
//交换机的名称
public static final String EXCHANGE_NAME = "direct_logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
//声明一个交换机
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
//声明一个队列
channel.queueDeclare("console",false,false,false,null);
//绑定交换机 1队列名称 2交换机名称 3路由key
channel.queueBind("console",EXCHANGE_NAME,"error");
//接收消息
DeliverCallback deliverCallback =(consumerTag, message)->{
System.out.println("Direct消费者2控制台打印接收到的消息:"+new String(message.getBody(),"UTF-8"));
};
//消费者取消消息时回调接口
channel.basicConsume("console",true,deliverCallback,consumerTag ->{});
}
}
运行生产者和消费者,并输入数据进行测试,观察两个消费者接收消息的结果:
生产者
direct消费者1接收结果
direct消费者2收到的消息:
比方说我们想接收的日志类型有info.base 和info.advantage,某个队列只想info.base的消息,那这个时候direct就办不到了。这个时候
就只能使用topic模式
发送到类型是topic交换机的消息的routingkey 不能随意写,必须满足一定的要求, 它必须是一个单
词列表,以点号分隔开。这些单词可以是任意单词,比如说: “stock.usd.nyse”, “nyse.vmw”,
“quick.orange.rabbit” .这种类型的。当然这个单词列表最多不能超过255个字节。
在这个规则列表中,其中有两个替换符是大家需要注意的
*(星号)可以代替一个单词
#(井号)可以替代零个或多个单词
当队列绑定关系是下列这种情况时需要引起注意
当一个队列绑定键是#那么这个队列将接收所有数据,就有点像fanout了
如果队列绑定键当中没有#和*出现,那么该队列绑定类型就是direct了
书写topic模式生产者代码
public class TopicProducter {
//交换机的名称
public static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
Map<String,String> bindingKey = new HashMap<>();
bindingKey.put("qw.orange.rabbbit","q1和q2都收到");
bindingKey.put("lazy.orange.ele","q1和q2都收到");
bindingKey.put("quick.orange.fox","被q1收到");
bindingKey.put("lazy.br.fox","被q2收到");
bindingKey.put("lazy.oki.rabbit","虽然满足两个绑定但只被队列q2接收一次");
bindingKey.put("quick.br.fox","不匹配任何绑定不会被任何队列收到");
bindingKey.put("quick.orange.mal.rabbit","四个单词不满足任何绑定");
bindingKey.put("lazy.orange.male.rabbit","四个单词满足q2");
for(Map.Entry<String,String> bindingkeyEntry : bindingKey.entrySet()){
String routingkey = bindingkeyEntry.getKey();
String message = bindingkeyEntry.getValue();
channel.basicPublish(EXCHANGE_NAME,routingkey,null,message.getBytes("UTF-8"));
System.out.println("生产者发出消息"+message);
}
}
}
topic消费者1:
public class TopicConsumer1 {
//交换机的名称
public static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
//声明一个交换机
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//声明一个队列
String queueName = "Q1";
channel.queueDeclare(queueName,false,false,false,null);
//绑定交换机 1队列名称 2交换机名称 3路由key
channel.queueBind(queueName,EXCHANGE_NAME,"*.orange.*");
System.out.println("待机中...");
//接收消息
DeliverCallback deliverCallback =(consumerTag, message)->{
System.out.println(new String(message.getBody(),"UTF-8"));
System.out.println("接收队列"+queueName+"->绑定键"+message.getEnvelope().getRoutingKey());
};
//消费者取消消息时回调接口
channel.basicConsume(queueName,true,deliverCallback,consumerTag ->{});
}
}
topic消费者2:
public class TopicConsumer2 {
//交换机的名称
public static final String EXCHANGE_NAME = "topic_logs";
public static void main(String[] args) throws Exception{
Channel channel = Rabbitmqutil.getChannel();
//声明一个交换机
channel.exchangeDeclare(EXCHANGE_NAME, "topic");
//声明一个队列
String queueName = "Q2";
channel.queueDeclare(queueName,false,false,false,null);
//绑定交换机 1队列名称 2交换机名称 3路由key
channel.queueBind(queueName,EXCHANGE_NAME,"*.*.rabbit");
channel.queueBind(queueName,EXCHANGE_NAME,"lazy.#");
System.out.println("待机中...");
//接收消息
DeliverCallback deliverCallback =(consumerTag, message)->{
System.out.println(new String(message.getBody(),"UTF-8"));
System.out.println("接收队列"+queueName+"->绑定键"+message.getEnvelope().getRoutingKey());
};
//消费者取消消息时回调接口
channel.basicConsume(queueName,true,deliverCallback,consumerTag ->{});
}
}
运行生产者与消费者并由生产者发送消息数据,观察消费者1和消费者2收到消息的情况:
不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。在绑定Queue与Exchange时指定一组键值对;当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers属性是一个键值对,可以是Hashtable,键值对的值可以是任何类型。而fanout,direct,topic 的路由键都需要要字符串形式的。