RabbitMQ-交换机

Exchanges

  • RabbitMQ消息传递模型的核心思想是:生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递传递到了哪些队列中
  • 生产者只能将消息发送到交换机(exchange),交换机一方面它接收来自生产者的消息,另一方面将它们推入队列。
  • 交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把他们放到许多队列中还是说应该丢弃它们。这由交换机的类型来决定

Exchange类型

  • 直接(direct)
  • 主题(topic)
  • 标题(headers)
  • 扇出(fanout)

无名 exchange

  • 第一个参数是交换机的名称,空字符串表示默认或无名称交换机
  • 消息能路由发送到队列中其实是由 routingKey(bindingkey)绑定key指定的,如果key存在
java 复制代码
channel.basicPublish("", "hello", null, message.getBytes());

临时队列

  • 没有持久化标志的队列,一旦我们断开了消费者的连接,队列将被自动删除
java 复制代码
// 创建临时队列的方式如下
//得到一个随机名称的队列
String queueName = channel.queueDeclare().getQueue();

绑定(bindings)

  • binding 其实是 exchange 和 queue 之间的桥梁,它指明exchange和哪个队列进行绑定

Fanout

  • exchange接收到的所有消息广播 到它知道的所有队列
  • 生产者
java 复制代码
public class EmitLog {
    private static final String EXCHANGE_NAME = "logs";
    
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        
        /**
         * 声明一个 exchange
         * 1.exchange 的名称
         * 2.exchange 的类型
         */
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入信息");
        while (sc.hasNext()) {
            String message = sc.nextLine();
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
            System.out.println("生产者发出消息" + message);
        }
    }
}
  • 消费者
java 复制代码
public class ReceiveLogs01 {
    private static final String EXCHANGE_NAME = "logs";
    
    public static void main(String[] argv) throws Exception {
       
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
        /**
         * 生成一个临时的队列 队列的名称是随机的
         * 当消费者断开和该队列的连接时 队列自动删除
         */
        String queueName = channel.queueDeclare().getQueue();
        
        //把该临时队列绑定我们的 exchange 其中 routingkey(也称之为 binding key)为空字符串
        channel.queueBind(queueName, EXCHANGE_NAME, "");
        System.out.println("等待接收消息,把接收到的消息打印在屏幕.....");
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("控制台打印接收到的消息"+message);
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
    }
}

Direct

  • Fanout这种交换类型并不能给我们带来很大的灵活性,它只能进行无意识的全部广播
  • 使用direct exchange这种类型来进行替换,这种类型的工作方式是,消息只发送到它绑定的routingKey队列中去
  • 例如:队列Q1绑定键为orange,队列Q2绑定键有两个:一个绑定键为black,另一个绑定键为green。在这种绑定情况下,生产者发布消息到exchange上,绑定键为orange的消息会被发布到队列Q1。绑定键为black或green和的消息会被发布到队列Q2,其他消息类型的消息将被丢弃
  • 多重绑定: exchange的绑定类型是direct,但是它绑定的多个队列的key 如果都相同,在这种情况下虽然绑定类型是direct但是它表现的就和fanout有点类似了,就跟广播差不多
  • 示例
java 复制代码
//生产者
public class EmitLogDirect {
    
    private static final String EXCHANGE_NAME = "direct_logs";
    
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        
        //创建多个 bindingKey
        Map<String, String> bindingKeyMap = new HashMap<>();
        bindingKeyMap.put("info", "普通 info 信息");
        bindingKeyMap.put("warning", "警告 warning 信息");
        bindingKeyMap.put("error", "错误 error 信息");
        //debug 没有消费者接收这个消息 所有就丢失了
        bindingKeyMap.put("debug", "调试 debug 信息");
        
        for (Map.Entry<String, String> bindingKeyEntry : bindingKeyMap.entrySet()) {
            String bindingKey = bindingKeyEntry.getKey();
            String message = bindingKeyEntry.getValue();
            channel.basicPublish(EXCHANGE_NAME, bindingKey, null,
                    message.getBytes("UTF-8"));
            System.out.println("生产者发出消息:" + message);
        }
    }
}
java 复制代码
//消费者
public class ReceiveLogsDirect01 {
    private static final String EXCHANGE_NAME = "direct_logs";
    
    public static void main(String[] argv) throws Exception {
        
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        String queueName = "console";
        channel.queueDeclare(queueName, false, false, false, null);
        channel.queueBind(queueName, EXCHANGE_NAME, "info");
        channel.queueBind(queueName, EXCHANGE_NAME, "warning");
        
        System.out.println("等待接收消息.....");
       
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" 接 收 绑 定 键 :"+delivery.getEnvelope().getRoutingKey()+", 消息:"+message);
        };
        
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});
    }
}

Topics

尽管使用direct交换机改进了我们的系统,但是它仍然存在局限性,比方说我们想接收的日志类型有info.base 和info.advantage,某个队列只想info.base的消息,那这个时候 direct就办不到了。这个时候就只能使用topic类型

topic的要求

  • 发送到类型是topic交换机的消息的routing_key不能随意写,必须满足一定的要求,它必须是一个单词列表,以点号分隔开。这些单词可以是任意单词,比如说:"stock.usd.nyse","nyse.vmw", "quick.orange.rabbit"这种类型的
  • 单词列表最多不能超过255个字节

两个替换符

  • *(星号)可以代替一个单词
  • #(井号)可以替代零个或多个单词
  • 当一个队列绑定键是#,那么这个队列将接收所有数据,就有点像 fanout 了
  • 如果队列绑定键当中没有#和*出现,那么该队列绑定类型就是 direct 了

routingkey示例

  • Q1-->绑定的是中间带 orange 带 3 个单词的字符串(*.orange.*)
  • Q2-->绑定的是结尾单词是 rabbit 的三个单词 (*.*.rabbit)和第一个单词是 lazy 的多个单词(lazy.#)
java 复制代码
//生产者
public class EmitLogTopic {
    
    private static final String EXCHANGE_NAME = "topic_logs";
    
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        
        /**
         * Q1-->绑定的是
         * 中间带 orange 带 3 个单词的字符串(*.orange.*)
         * Q2-->绑定的是
         * 最后一个单词是 rabbit 的 3 个单词(*.*.rabbit)
         * 第一个单词是 lazy 的多个单词(lazy.#)
         *
         */
        Map<String, String> bindingKeyMap = new HashMap<>();
        bindingKeyMap.put("quick.orange.rabbit", "被队列 Q1Q2 接收到");
        bindingKeyMap.put("lazy.orange.elephant", "被队列 Q1Q2 接收到");
        bindingKeyMap.put("quick.orange.fox", "被队列 Q1 接收到");
        bindingKeyMap.put("lazy.brown.fox", "被队列 Q2 接收到");
        bindingKeyMap.put("lazy.pink.rabbit", "虽然满足两个绑定但只被队列 Q2 接收一次");
        bindingKeyMap.put("quick.brown.fox", "不匹配任何绑定不会被任何队列接收到会被丢弃");
        bindingKeyMap.put("quick.orange.male.rabbit", "是四个单词不匹配任何绑定会被丢弃");
        bindingKeyMap.put("lazy.orange.male.rabbit", "是四个单词但匹配 Q2");
        
        for (Map.Entry<String, String> bindingKeyEntry : bindingKeyMap.entrySet()) {
            String bindingKey = bindingKeyEntry.getKey();
            String message = bindingKeyEntry.getValue();
            channel.basicPublish(EXCHANGE_NAME, bindingKey, null,
                    message.getBytes("UTF-8"));
            System.out.println("生产者发出消息" + message);
        }
    }
}
java 复制代码
//消费者 Q1
public class ReceiveLogsTopic01 {
    
    private static final String EXCHANGE_NAME = "topic_logs";
    
    public static void main(String[] argv) throws Exception {
        
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        
        //声明 Q1 队列与绑定关系
        String queueName="Q1";
        channel.queueDeclare(queueName, false, false, false, null);
        channel.queueBind(queueName, EXCHANGE_NAME, "*.orange.*");
        System.out.println("等待接收消息.....");
        
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" 接 收 队 列 :"+queueName+" 绑 定 键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message);
        };
        
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
    }
}
java 复制代码
//消费者 Q2
public class ReceiveLogsTopic02 {
    
    private static final String EXCHANGE_NAME = "topic_logs";
    
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "topic");
        
        //声明 Q2 队列与绑定关系
        String queueName="Q2";
        channel.queueDeclare(queueName, false, false, false, null);
        channel.queueBind(queueName, EXCHANGE_NAME, "*.*.rabbit");
        channel.queueBind(queueName, EXCHANGE_NAME, "lazy.#");
        System.out.println("等待接收消息.....");
        
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println(" 接 收 队 列 :"+queueName+" 绑 定 键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message);
        };
        
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});
    }
}
相关推荐
m0_687399841 小时前
telnet localhost 15672 RabbitMQ “Connection refused“ 错误表示目标主机拒绝了连接请求。
分布式·rabbitmq
陌上丨2 小时前
生产环境分布式锁的常见问题和解决方案有哪些?
分布式
新新学长搞科研2 小时前
【智慧城市专题IEEE会议】第六届物联网与智慧城市国际学术会议(IoTSC 2026)
人工智能·分布式·科技·物联网·云计算·智慧城市·学术会议
Ronin3052 小时前
日志打印和实用 Helper 工具
数据库·sqlite·rabbitmq·文件操作·uuid生成
泡泡以安2 小时前
Scrapy分布式爬虫调度器架构设计说明
分布式·爬虫·scrapy·调度器
没有bug.的程序员3 小时前
RocketMQ 与 Kafka 深度对垒:分布式消息引擎内核、事务金融级实战与高可用演进指南
java·分布式·kafka·rocketmq·分布式消息·引擎内核·事务金融
上海锟联科技4 小时前
250MSPS DAS 在地铁监测中够用吗?——来自上海锟联科技的工程实践
分布式·科技·分布式光纤传感·das解调卡·光频域反射·das
岁岁种桃花儿4 小时前
深度解析DolphinScheduler核心架构:搭建高可用Zookeeper集群
linux·分布式·zookeeper
yxy___4 小时前
达梦分布式集群DPC_影子和实体副本相互转换(DEM)_yxy
分布式·dem·影子副本
努力有什么不好4 小时前
Hadoop3.2.2伪分布式搭建
大数据·hadoop·分布式