RabbitMQ交换机

交换机

概念

RabbitMQ 消息传递模型的核心思想是:生产者生产的消息从不会直接发送到队列。实际上,通常生产者甚至都不知道这些消息传递到了哪些队列中。

相反,生产者只能将消息发送到交换机(exchange),**交换机工作的内容非常简单,一方面接收来自生产者的消息,另一方面将他们推入队列。交换机必须明确知道如何处理收到的消息。**是应该把这些消息放到特定队列还是说把他们放到许多队列中还是说应该丢弃他们。这就得由交换机的类型来决定。

类型

总共有以下类型:

直接(direct,路由),主题(topic),标题(headers),扇出(fanout,发布订阅模式)

bindings

绑定时交换机和队列之间的桥梁关系。也可以这么理解:队列只对它绑定的交换机的消息感兴趣。绑定用参数:routingKey来表示也可称该参数为 binding key,创建绑定我们用代码:channelQueueBind(queueName, EXCHANGE_NAME, "routingKey");绑定之后的意义由其交换类型决定。

Fanout

概念

Fanout 这种类型非常简单。它是将接收到的所有消息广播到它知道的所有队列中。系统中默认有些 exchange 类型。

生产者
java 复制代码
package com.my.rabbitmq.five;
​
import com.my.utils.RabbitMQUtils;
import com.rabbitmq.client.Channel;
​
import java.util.Scanner;
​
/**
 * @author 林允
 * @version 1.0
 * @description: TODO
 * @date 2023/11/29 15:33
 */
// 发送消息
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 sc = new Scanner(System.in);
​
        while (sc.hasNext()){
            String message = sc.next();
            channel.basicPublish(Exchange_NAME, "", null, message.getBytes("UTF-8"));
            System.out.println("生产者发出消息" + message);
        }
    }
​
}
消费者
java 复制代码
package com.my.rabbitmq.five;
​
import com.my.utils.RabbitMQUtils;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
​
/**
 * @author 林允
 * @version 1.0
 * @description: TODO
 * @date 2023/11/29 15:34
 */
​
// 消息接收
public class ReceiveLogs01 {
​
    // 交换机的名称
    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("接收消息:" + new String(message.getBody(), "UTF-8"));
        };
        CancelCallback cancelCallback = (consumerTag) -> {
            System.out.println("接收失败");
        };
        channel.basicConsume(queueName, true, deliverCallback, cancelCallback);
    }
​
}
java 复制代码
package com.my.rabbitmq.five;
​
import com.my.utils.RabbitMQUtils;
import com.rabbitmq.client.CancelCallback;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
​
/**
 * @author 林允
 * @version 1.0
 * @description: TODO
 * @date 2023/11/29 16:01
 */
​
// 消息接收
public class ReceiveLogs02 {
​
    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 queue = channel.queueDeclare().getQueue();
        // 将队列和交换机进行绑定
        channel.queueBind(queue, EXCHANGE_NAME, "");
        System.out.println("ReceiveLogs02 等待接收消息,把接收到的消息打印在屏幕上....");
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("接收到消息:" + new String(message.getBody(), "UTF-8"));
        };
​
        CancelCallback cancelCallback = (consumerTag) -> {
            System.out.println("接收失败" + consumerTag);
        };
        channel.basicConsume(queue, true, deliverCallback, cancelCallback);;
​
    }
​
}

Direct

Fanout这种交换类型并不能给我们带来很大的灵活性 - 它只能进行无意识的广播,在这里我们将使用direct 这种类型来进行替换,这种类型的工作方式是,消息只去到它绑定的routingKey队列中去。

如果与direct交换机绑定的多个队列的 routingKey 如果都相同 ,在这种情况下虽然绑定类型是 direct ,但是它表现的就和 fanout 差不多了

routingKey 相同 --> fanout交换机

routingKey 不相同 --> direct交换机

Topics

尽管使用 direct 交换机根据 routingKey 发到指定的队列,但是它仍然存在局限性,比如所我们只想接收 info.base 的消息,但是只要 routingKey 是 info 的就会全部接收,这个时候就只能使用topic类型

要求

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

在这个规则列表中,其中有两个替换符是需要注意的

*(星号)可以代替一个单词

#(井号)可以替代零个或多个单词

当队列绑定关系是下列这种情况时需要注意

当一个队列绑定键是 # ,那么这个队列将接收所有数据,就有点像 fanout

如果队列绑定键中没有 # 和 * 出现,那么该队列绑定的类型就是 direct

消费者
java 复制代码
package com.my.rabbitmq.six;
​
import com.my.utils.RabbitMQUtils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
​
/**
 * @author 林允
 * @version 1.0
 * @description: TODO
 * @date 2023/11/29 16:28
 */
// 声明主题交换机及相关队列
// 消费者C1
public class ReceiveLogsTopic01 {
​
    // 交换机的名称
    public static final String EXCHANGE_NAME = "topic_logs";
​
    // 接收消息
    public static void main(String[] args) throws Exception{
        Channel channel = RabbitMQUtils.getChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        // 声明队列
        String queueName = "Q1";
        channel.queueDeclare(queueName, false, false, false, null);
        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 -> {
            System.out.println("接收消息失败" );
        });
    }
​
}
java 复制代码
package com.my.rabbitmq.six;
​
import com.my.utils.RabbitMQUtils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;
​
/**
 * @author zhupanlin
 * @version 1.0
 * @description: TODO
 * @date 2023/11/30 12:24
 */
public class ReceiveLogsTopic02 {
​
    // 交换机名称
    public static final String EXCHANGE_NAME = "topic_logs";
​
    public static void main(String[] args) throws Exception{
​
        Channel channel = RabbitMQUtils.getChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        // 声明队列
        String queueName = "Q2";
        channel.queueDeclare(queueName, false, false,false, null);
        channel.queueBind(queueName, EXCHANGE_NAME, "lazy.#");
​
        System.out.println("等待接收消息");
​
        DeliverCallback deliverCallback = (consumerTag, message) -> {
            System.out.println("队列" + queueName + "的routingKey:" + message.getEnvelope().getRoutingKey());
            System.out.println("接收到消息:" + new String(message.getBody()));
        };
        
        // 接收消息
        channel.basicConsume(queueName, true, deliverCallback, (consumerTag) -> {
            System.out.println(consumerTag + "接收消息失败");
        });
​
    }
​
​
}
相关推荐
Lee川40 分钟前
mini-cursor 揭秘:从 Tool 定义到 Agent 循环的完整实现
前端·人工智能·后端
一直不明飞行1 小时前
Java的equals(),hashCode()应该在什么时候重写
java·开发语言·jvm
REDcker1 小时前
有限状态机与状态模式详解 FSM建模Java状态模式与C++表驱动模板实践
java·c++·状态模式
你的保护色2 小时前
【无标题】
java·服务器·网络
basketball6162 小时前
C++ 构造函数完全指南:从入门到进阶
java·开发语言·c++
淘矿人2 小时前
Claude辅助DevOps实践
java·大数据·运维·人工智能·算法·bug·devops
星浩AI3 小时前
OpenHuman 对比 OpenClaw、Hermes Agent
人工智能·后端·agent
小江的记录本3 小时前
【Java基础】泛型:泛型擦除、通配符、上下界限定(附《思维导图》+《面试高频考点清单》)
java·数据结构·后端·mysql·spring·面试·职场和发展
来恩10033 小时前
请求转发与响应重定向的使用
java
@杰克成3 小时前
Java学习30
java·开发语言·学习