《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 :表示只要有键值对匹配就能接受到消息

相关推荐
P.H. Infinity7 小时前
【RabbitMQ】03-交换机
分布式·rabbitmq
孤蓬&听雨15 小时前
RabbitMQ自动发送消息工具(自动化测试RabbitMQ)
分布式·测试工具·自动化·rabbitmq·自动发送消息
呼啦啦啦啦啦啦啦啦15 小时前
RabbitMQ 七种工作模式介绍
分布式·rabbitmq
qq_2037694915 小时前
win11安装最新rabbitmq
分布式·rabbitmq·ruby
Amagi.15 小时前
RabbitMQ的解耦、异步、削峰是什么?
分布式·rabbitmq
小码哥呀21 小时前
RabbitMQ设置消息过期时间
分布式·rabbitmq
斑驳竹影21 小时前
rabbitMQ RabbitTemplate 发送消息
java·rabbitmq·java-rabbitmq
陈奕迅本讯1 天前
rabbitMQ
分布式·rabbitmq
田猿笔记1 天前
RabbitMQ 实现消息队列负载均衡
分布式·rabbitmq·负载均衡
HoHeHa1 天前
RabbitMQ 管理平台(控制中心)的介绍
java·rabbitmq