《RabbitMQ篇》交换机基本概览

生产者都是把消息给交换机,由交换机分发给消息队列。

routingKey:路由键,也可称为绑定,是交换机和队列之间的桥梁,交换机会根据routingKey来把消息转发到对应的队列。

Fanout

不处理路由键。你只需要简单的将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。

通用工具类:

java 复制代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class RabbitMQUtil {
    public static Channel getChannel() throws Exception {
        //得到工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setPort(5672);
        factory.setUsername("guest");
        factory.setPassword("guest");
        //生成连接
        Connection connection = factory.newConnection();
        //获取信道
        return connection.createChannel();
    }
}

生产者代码:创建一个Fanout Exchange。

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.util.Scanner;

public class FanoutProducer {

    private final static String EXCHANGE_NAME = "LogExchange";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String message = scanner.nextLine();
            channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes());
        }
    }
}

两个消费者代码:创建队列去绑定交换机

java 复制代码
public class Consumer1 {
    private final static String FILE_QUEUE = "FileQueue";
    private final static String EXCHANGE_NAME = "LogExchange";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();
        channel.queueDeclare(FILE_QUEUE, false, false, false, null);
        channel.queueBind(FILE_QUEUE, EXCHANGE_NAME, "");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            System.out.printf("消息:%s,存储到磁盘%n", new String(delivery.getBody()));
        };
        channel.basicConsume(FILE_QUEUE, true, deliverCallback, consumerTag -> {});
    }
}
java 复制代码
public class Consumer2 {
    private final static String CONSOLE_QUEUE = "ConsoleQueue";
    private final static String EXCHANGE_NAME = "LogExchange";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();

        channel.queueDeclare(CONSOLE_QUEUE, false, false, false, null);
        channel.queueBind(CONSOLE_QUEUE, EXCHANGE_NAME, "");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            System.out.printf("消息:%s,输出到控制台%n", new String(delivery.getBody()));
        };
        channel.basicConsume(CONSOLE_QUEUE, true, deliverCallback, consumerTag -> {});
    }
}

运行结果图如下。总结:在Fanout模式下,生产者生产消息发布到交换机后,交换机会把消息发送给所有与之绑定的队列,在该模式下,即使设置了routingKey也会无视它。

Direct

处理路由键。需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。

生产者代码:创建一个Direct交换机,发送消息到交换机同时指定routingKey。

java 复制代码
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;

import java.util.Scanner;

public class DirectProducer {
    private final static String EXCHANGE_NAME = "LogExchange-Direct";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String message = scanner.nextLine();
            channel.basicPublish(EXCHANGE_NAME, "consoleRouteKey", null, message.getBytes());
        }
    }
}

消费者代码:让队列和交换机绑定,同时设置routingKey。

java 复制代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer1 {
    private final static String FILE_QUEUE = "FileQueue";
    private final static String EXCHANGE_NAME = "LogExchange-Direct";


    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();
        channel.queueDeclare(FILE_QUEUE, false, false, false, null);
        channel.queueBind(FILE_QUEUE, EXCHANGE_NAME, "fileRouteKey");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            System.out.printf("消息:%s,存储到磁盘%n", new String(delivery.getBody()));
        };
        channel.basicConsume(FILE_QUEUE, true, deliverCallback, consumerTag -> {});
    }
}
java 复制代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer2 {
    private final static String CONSOLE_QUEUE = "ConsoleQueue";
    private final static String EXCHANGE_NAME = "LogExchange-Direct";


    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();

        channel.queueDeclare(CONSOLE_QUEUE, false, false, false, null);
        channel.queueBind(CONSOLE_QUEUE, EXCHANGE_NAME, "consoleRouteKey");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            System.out.printf("消息:%s,输出到控制台%n", new String(delivery.getBody()));
        };
        channel.basicConsume(CONSOLE_QUEUE, true, deliverCallback, consumerTag -> {});
    }
}

执行结果如图,总结:在Direct模式下,交换机会根据routingKey把消息给相关的队列,如果没有这样的队列,消息会被丢弃。

Topic

给定的routingKey,与 交换机和队列之间设置的routingKey 根据模式匹配(类似正则)转发到对应的队列。

该模式下的routingKey的命名是一个单词列表,以点号分隔开。例如:"log.error.console"。其中可以用 *(星号)可以代替一个位置,#(井号)可以替代零个或多个位置。

比如:现在队列Q1和交换机之间设置的routingKey为 *.orange.*

队列Q2和交换机之间设置的routingKey为 *.*.rabbit*lazy.#

例如 说明
quick.orange.rabbit 被队列 Q1Q2 接收到
azy.orange.elephant 被队列 Q1Q2 接收到
quick.orange.fox 被队列 Q1 接收到
lazy.brown.fox 被队列 Q2 接收到
lazy.pink.rabbit 虽然满足两个绑定但只被队列 Q2 接收一次
quick.brown.fox 不匹配任何绑定不会被任何队列接收到会被丢弃
quick.orange.male.rabbit 是四个单词不匹配任何绑定会被丢弃
lazy.orange.male.rabbit 是四个单词但匹配 Q2

实际测试代码

java 复制代码
import com.rabbitmq.client.Channel;

import java.util.HashMap;
import java.util.Map;

public class TopicProducer {
    private final static String EXCHANGE_NAME = "LogExchange-Topic";

    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();

        HashMap<String, String> bindingKeyMap = new HashMap<>();
        bindingKeyMap.put("quick.orange.rabbit", "1.被队列 Q1Q2 接收到");
        bindingKeyMap.put("lazy.orange.elephant", "2.被队列 Q1Q2 接收到");
        bindingKeyMap.put("quick.orange.fox", "3.被队列 Q1 接收到");
        bindingKeyMap.put("lazy.brown.fox", "4.被队列 Q2 接收到");
        bindingKeyMap.put("lazy.pink.rabbit", "5.虽然满足两个绑定但只被队列 Q2 接收一次");
        bindingKeyMap.put("quick.brown.fox", "6.不匹配任何绑定不会被任何队列接收到会被丢弃");
        bindingKeyMap.put("quick.orange.male.rabbit", "7.是四个单词不匹配任何绑定会被丢弃");
        bindingKeyMap.put("lazy.orange.male.rabbit", "8.是四个单词但匹配 Q2");
        for (Map.Entry<String,String> bindingKeyEntry : bindingKeyMap.entrySet()){
            String routingKey = bindingKeyEntry.getKey();
            String message = bindingKeyEntry.getValue();
            channel.basicPublish(EXCHANGE_NAME,routingKey,null,message.getBytes("UTF-8"));
            System.out.println("生产者发出消息:"+message);
        }
    }
}
java 复制代码
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DeliverCallback;

public class Consumer1 {
    private final static String FILE_QUEUE = "FileQueue";
    private final static String EXCHANGE_NAME = "LogExchange-Topic";


    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();
        channel.queueDeclare(FILE_QUEUE, false, false, false, null);
        channel.queueBind(FILE_QUEUE, EXCHANGE_NAME, "*.orange.*");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            System.out.printf("消息:%s,存储到磁盘%n", new String(delivery.getBody()));
        };
        channel.basicConsume(FILE_QUEUE, true, deliverCallback, consumerTag -> {});
    }
}
java 复制代码
public class Consumer2 {
    private final static String CONSOLE_QUEUE = "ConsoleQueue";
    private final static String EXCHANGE_NAME = "LogExchange-Topic";


    public static void main(String[] args) throws Exception {
        Channel channel = RabbitMQUtil.getChannel();

        channel.queueDeclare(CONSOLE_QUEUE, false, false, false, null);
        channel.queueBind(CONSOLE_QUEUE, EXCHANGE_NAME, "*.*.rabbit");
        channel.queueBind(CONSOLE_QUEUE, EXCHANGE_NAME, "lazy.#");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            System.out.printf("消息:%s,输出到控制台%n", new String(delivery.getBody()));
        };
        channel.basicConsume(CONSOLE_QUEUE, true, deliverCallback, consumerTag -> {});
    }
}

不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。在绑定 Queue 与 Exchange 时指定一组键值对;当消息发送到RabbitMQ 时会取到该消息的 headers 与 Exchange 绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。headers 属性是一个键值对,可以是 Hashtable,键值对的值可以是任何类型。而 fanout,direct,topic 的路由键都需要要字符串形式的。

匹配规则 x-match 有下列两种类型:

x-match = all :表示所有的键值对都匹配才能接受到消息

x-match = any :表示只要有键值对匹配就能接受到消息

相关推荐
浩哲Zhe18 小时前
RabbitMQ
java·分布式·rabbitmq
Allen Bright20 小时前
RabbitMQ中的Topic模式
分布式·rabbitmq
Allen Bright1 天前
Spring Boot 整合 RabbitMQ:手动 ACK 与 QoS 配置详解
spring boot·rabbitmq·java-rabbitmq
一路狂飙的猪1 天前
RabbitMQ的工作模型
分布式·rabbitmq
来一杯龙舌兰1 天前
【RabbitMQ】RabbitMQ保证消息不丢失的N种策略的思想总结
分布式·rabbitmq·ruby·持久化·ack·消息确认
Allen Bright2 天前
Spring Boot 整合 RabbitMQ:从入门到实践
spring boot·rabbitmq·java-rabbitmq
bug_null2 天前
RabbitMQ消息可靠性保证机制7--可靠性分析-rabbitmq_tracing插件
分布式·rabbitmq
kingbal2 天前
RabbitMQ:添加virtualHost
分布式·rabbitmq
04Koi.3 天前
Java项目--仿RabbitMQ的消息队列--虚拟主机设计
分布式·rabbitmq
04Koi.3 天前
Java项目--仿RabbitMQ的消息队列--网络通信协议设计
分布式·rabbitmq