赞
踩
RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中。
相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单,一方面它接收来自生产者的消息,另一方面将它们推入队列。交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把他们到许多队列中还是说应该丢弃它们。这就的由交换机的类型来决定。
总共有以下类型:
直接(direct), 主题(topic) ,标题(headers) , 扇出(fanout)
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
第一个参数是交换机的名称。空字符串表示默认或无名称交换机:消息能路由发送到队列中其实是由 routingKey(bindingkey)绑定 key 指定的,如果它存在的话
每当我们连接到 Rabbit 时,我们都需要一个全新的空队列,为此我们可以创建一个具有随机名称的队列,或者能让服务器为我们选择一个随机队列名称那就更好了。其次一旦我们断开了消费者的连接,队列将被自动删除。
//声明一个队列,临时队列
//生成一个临时队列,队列的名称随机
//当消费者断开与队列的连接时,队列就是自动删除
String queueName = channel.queueDeclare().getQueue();
binding是 exchange 和 queue 之间的桥梁,它告诉我们 exchange 和那个队列进行了绑定关系。比如说下面这张图告诉我们的就是 X 与 Q1 和 Q2 进行了绑定
Fanout 这种类型非常简单。正如从名称中猜到的那样,它是将接收到的所有消息广播到它知道的所有队列中。系统中默认有些 exchange 类型
package com.rabbitmq.exchange.fanout; import com.rabbitmq.client.Channel; import com.rabbitmq.client.DeliverCallback; import com.rabbitmq.workqueues.RabbitMqUtils; /** * 消费者 */ public class Recevice { public static final String EXCHANGE_NAME="logs"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); //声明一个交换机 channel.exchangeDeclare(EXCHANGE_NAME,"fanout"); //声明一个队列,临时队列 //生成一个临时队列,队列的名称随机 //当消费者断开与队列的连接时,队列就是自动删除 String queueName = channel.queueDeclare().getQueue(); //绑定交换机与队列 channel.queueBind(queueName,EXCHANGE_NAME,""); System.out.println("等待接收消息,把收到的消息打印在屏幕上。。。。。。。"); //接受消息 DeliverCallback deliverCallback=(consumerTag,message)->{ System.out.println("Recevice控制台打印接收到消息"+new String(message.getBody(),"UTF-8")); }; //消费取消消息回调接口 channel.basicConsume(queueName,true,deliverCallback,consumerTag -> {}); } }
package com.rabbitmq.exchange.fanout; import com.rabbitmq.client.Channel; import com.rabbitmq.client.DeliverCallback; import com.rabbitmq.workqueues.RabbitMqUtils; /** * 消费者 */ public class Recevice2 { //交换机名称 public static final String EXCHANGE_NAME="logs"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); //声明一个交换机 channel.exchangeDeclare(EXCHANGE_NAME,"fanout"); //声明一个队列,临时队列 //生成一个临时队列,队列的名称随机 //当消费者断开与队列的连接时,队列就是自动删除 String queueName = channel.queueDeclare().getQueue(); //绑定交换机与队列 channel.queueBind(queueName,EXCHANGE_NAME,""); System.out.println("等待接收消息,把收到的消息打印在屏幕上。。。。。。。"); //接受消息 DeliverCallback deliverCallback=(consumerTag,message)->{ String message = new String(delivery.getBody(), "UTF-8"); File file = new File("C:\\work\\rabbitmq_info.txt"); FileUtils.writeStringToFile(file,message,"UTF-8"); System.out.println("数据写入文件成功"); }; //消费取消消息回调接口 channel.basicConsume(queueName,true,deliverCallback,consumerTag -> {}); } }
发送消息给两个消费者接收
package com.rabbitmq.exchange.fanout; import com.rabbitmq.client.Channel; import com.rabbitmq.workqueues.RabbitMqUtils; import java.nio.charset.StandardCharsets; import java.util.Scanner; /** * 生产者 */ public class EmitLog { //交换机名称 public static final String EXCHANGE_NAME="logs"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); channel.exchangeDeclare(EXCHANGE_NAME,"fanout"); Scanner scanner = new Scanner(System.in); while (scanner.hasNext()){ String next = scanner.next(); channel.basicPublish(EXCHANGE_NAME,"",null,next.getBytes(StandardCharsets.UTF_8)); System.out.println("生产者发出消息"+next); } } }
对此我们想做一些改变,例如我们希望将日志消息写入磁盘的程序仅接收严重错误(errros),而不存储哪些警告(warning)或信息(info)日志消息避免浪费磁盘空间。Fanout 这种交换类型并不能给我们带来很大的灵活性-它只能进行无意识的广播,在这里我们将使用 direct 这种类型来进行替换,这种类型的工作方式是,消息只去到它绑定的routingKey 队列中去。
当然如果 exchange 的绑定类型是 direct,但是它绑定的多个队列的 key 如果都相同,在这种情况下虽然绑定类型是 direct 但是它表现的就和 fanout 有点类似了,就跟广播差不多,如上图所示。
package com.rabbitmq.exchange.direct; import com.rabbitmq.client.BuiltinExchangeType; import com.rabbitmq.client.Channel; import com.rabbitmq.client.DeliverCallback; import com.rabbitmq.workqueues.RabbitMqUtils; /** * 消费者 */ public class directRevice { //交换机名称 private static final String EXCHANGE_NAME="direct_logs"; private static final String QUEUE_NAME="console"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); //声明交换机 channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); //声明一个队列 channel.queueDeclare(QUEUE_NAME,false,false,false,null); //将队列与交换机绑定 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"info"); channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"waring"); //接收消息 DeliverCallback deliverCallback=(consumerTag,message)->{ System.out.println("消费者一控制打印收到消息:"+new String(message.getBody())+" "+consumerTag); }; //消费者取消消息时回调接口 channel.basicConsume(QUEUE_NAME,true,deliverCallback,consumerTag -> {}); } }
package com.rabbitmq.exchange.direct; import com.rabbitmq.client.BuiltinExchangeType; import com.rabbitmq.client.Channel; import com.rabbitmq.client.DeliverCallback; import com.rabbitmq.workqueues.RabbitMqUtils; /** * 消费者 */ public class directRevice1 { //交换机名称 private static final String EXCHANGE_NAME="direct_logs"; private static final String QUEUE_NAME="disk"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); //声明交换机 channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); //声明一个队列 channel.queueDeclare(QUEUE_NAME,false,false,false,null); //将队列与交换机绑定 channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"error"); //接收消息 DeliverCallback deliverCallback=(consumerTag,message)->{ System.out.println("消费者一控制打印收到消息:"+new String(message.getBody())+" "+consumerTag); }; //消费者取消消息时回调接口 channel.basicConsume(QUEUE_NAME,true,deliverCallback,consumerTag -> {}); } }
package com.rabbitmq.exchange.direct; import com.rabbitmq.client.Channel; import com.rabbitmq.workqueues.RabbitMqUtils; import java.nio.charset.StandardCharsets; import java.util.Scanner; /** * 生产者 */ public class DirectLogs { private static final String EXCHANGE_NAME="direct_logs"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); Scanner scanner = new Scanner(System.in); while (scanner.hasNext()) { String message = scanner.next(); channel.basicPublish(EXCHANGE_NAME,message,null,message.getBytes(StandardCharsets.UTF_8)); System.out.println("生产者发送消息" + message); } } }
direct 交换机系统,但是它仍然存在局限性-比方说我们想接收的日志类型有info.base 和 info.advantage,某个队列只想 info.base 的消息,那这个时候 direct 就办不到了。这个时候就只能使用 topic 类型
发送到类型是 topic 交换机的消息的 routing_key 不能随意写,必须满足一定的要求,它必须是一个单词列表,以点号分隔开。这些单词可以是任意单词,比如说:“stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”.这种类型的。当然这个单词列表最多不能超过 255 个字节。
在这个规则列表中,其中有两个替换符是大家需要注意的
当队列绑定关系是下列这种情况时需要引起注意
/** * 主题模式 * 1.关于routing_key不能随意写,必须满足一下要求 * 1.1 单词列表,以点号隔开 例如 la.l.l 单词列表不能超过255个字节 * 1.1.1 *(星号) 可以替代一个单词 la.*.l ==la.l.l * 1.1.2 #(井号) 可以替代多个单词 la.# ==la.l.l */ public class Topic { //交换机名称 private static final String EXCHANGE_NAME = "topic_log"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); HashMap<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> stringStringEntry : map.entrySet()) { String key = stringStringEntry.getKey(); String value = stringStringEntry.getValue(); channel.basicPublish(EXCHANGE_NAME,key,null,value.getBytes(StandardCharsets.UTF_8)); System.out.println("生产者发送消息"+value); } } }
/** * 消费者一 */ public class TopicC1 { //交换机名称 private static final String EXCHANGE_NAME = "topic_log"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC); channel.queueDeclare("Q1",false,false,false,null); channel.queueBind("Q1",EXCHANGE_NAME,"*.orange.*"); System.out.println("等待接收消息。。。。。。。。"); channel.basicConsume("Q1",true,(consumerTag,message)->{ System.out.println("c1"+new String(message.getBody(),"UTF-8")); System.out.println("接收队列绑定的值"+message.getEnvelope().getRoutingKey()); }, consumerTag -> {}); } }
/** * 消费者二 */ public class TopicC2 { //交换机名称 private static final String EXCHANGE_NAME = "topic_log"; public static void main(String[] args) throws Exception { Channel channel = RabbitMqUtils.getChannel(); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC); channel.queueDeclare("Q2",false,false,false,null); channel.queueBind("Q2",EXCHANGE_NAME,"*.*.rabbit"); channel.queueBind("Q2",EXCHANGE_NAME,"lazy.#"); System.out.println("等待接收消息。。。。。。。。"); channel.basicConsume("Q2",true,(consumerTag,message)->{ System.out.println("c2"+new String(message.getBody(),"UTF-8")); System.out.println("接收队列绑定的值"+message.getEnvelope().getRoutingKey()); }, consumerTag -> {}); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。