【RabbitMQ-交换机】

一、简介

交换机的作用

接收生产者发送的消息并将这些消息路由给服务器中的队列

交换机的一些属性

  • Name:交换机名称

  • Durability:是否持久化。如果持久性,则RabbitMQ重启后,交换机还存在。

  • Auto-delete:当所有与之绑定的消息队列都完成了对此交换机的使用后,删掉它。

  • Arguments:扩展参数

二、Direct Exchange:直连交换机

2.1 基础

使用前提

消息中的路由键(routing key)如果和Binding中的binding key完全一致,交换机就将消息发到对应的队列中。

图示

解释 

这种模式就要生产者在发送消息的时候,为此消息添加一个路由键,在消费者获取时才能达到“按需索取”。

例如:将不同的级别的错误消息传递给不同的消费者,这就需要用到这种模式。

2.2 代码

生产者

public static void main(String[] args) throws Exception {
    // 创建ConnectionFactory
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    // 设置虚拟主机
    connectionFactory.setVirtualHost("/");
    // 创建Connection
    Connection connection = connectionFactory.newConnection();
    // 创建Channel
    Channel channel = connection.createChannel();
    // 交换机名称
    String exchangeName = "test_direct_exchange";
    // routing key
    String routingKey = "test.direct";
    // 发送消息
    String msg = "RabbitMQ Direct Exchange TEST";
    channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
}

消费者

public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setAutomaticRecoveryEnabled(true);
    connectionFactory.setNetworkRecoveryInterval(3000);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    // 交换机名称
    String exchangeName = "test_direct_exchange";
    // 交换机类型
    String exchangeType = "direct";
    // 消息队列名称
    String queueName = "test_direct_queue";
    // Routing Key(binding key)
    String routingKey = "test.direct";
    // 声明交换机
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    // 表示一个队列
    channel.queueDeclare(queueName, false, false, false, null);
    // 建立Exchange和Queue绑定关系
    channel.queueBind(queueName, exchangeName, routingKey);
    // 参数说明:队列名称、是否自动ACK、DefaultConsumer(消费者)
    channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("收到消息:" + new String(body));
        }
    });
}

三、Fanout Exchange:扇形交换机

3.1 基础

介绍

扇形交换机是最基本的交换机类型,它所能做的事情非常简单,即:广播消息。不同于直连交换机,路由键在此类型上不启任务作用。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息发送给这所有的N个队列。因为广播不需要“思考”,所以扇形交换机处理消息的速度也是所有的交换机类型里面最快的不处理路由键。

说明

  • 这种模式不需要Routing Key

  • 这种模式需要提前将Exchange与Queue进行绑定,一个Exchange可以绑定多个Queue,一个Queue可以同多个Exchange进行绑定。

  • 如果接受到消息的Exchange没有与任何Queue绑定,则消息会被抛弃。

图示

3.2 代码 

生产者

public static void main(String[] args) throws Exception {
    // 创建ConnectionFactory
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    // 设置虚拟主机
    connectionFactory.setVirtualHost("/");
    // 创建Connection
    Connection connection = connectionFactory.newConnection();
    // 创建Channel
    Channel channel = connection.createChannel();
    // 交换机名称
    String exchangeName = "test_fanout_exchange";
    // routing key(因为fanout交换机与routing key无关,所有设置为"")
    String routingKey = "";
    // 发送5条消息
    for (int i = 1; i <= 5; i++) {
        String msg = "RabbitMQ fanout Exchange  TEST" + i;
        channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
    }
    channel.close();
    connection.close();
}

消费者

public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setAutomaticRecoveryEnabled(true);
    connectionFactory.setNetworkRecoveryInterval(3000);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    // 交换机名称
    String exchangeName = "test_fanout_exchange";
    // 交换机类型
    String exchangeType = "fanout";
    // 消息队列名称
    String queueName = "test_fanout_queue";
    // fanout模式不以routing key为匹配规则,可以为空。
    String routingKey = " ";
    // 声明交换机
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    // 表示一个队列
    channel.queueDeclare(queueName, false, false, false, null);
    // 建立Exchange和Queue绑定关系
    channel.queueBind(queueName, exchangeName, routingKey);
    // 参数说明:队列名称、是否自动ACK、DefaultConsumer(消费者)
    channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("收到消息:" + new String(body));
        }
    });
}

四、Topic Exchange:主题交换机

4.1 基础

介绍

主题交换机(topic exchange)中,队列通过路由键绑定到交换机上,然后,交换机根据消息里的路由值,将消息路由给一个或多个绑定队列

扇型交换机和主题交换机异同:

  • 对于扇型交换机路由键是没有意义的,只要有消息,它都发送到它绑定的所有队列上。

  • 对于主题交换机,路由规则由路由键(binding key)决定,只有满足路由键(binding key)的规则,消息才可以路由到对应的队列。

图示

匹配规则 

  • *可以替代一个单词

  • # :可以替代零个或更多的单词

    只要能模糊匹配上就能将消息映射到队列中。当一个队列的绑定键为#的时候,这个队列将会无视消息的路由键,接收所有的消息。

4.2 代码

生产者

public static void main(String[] args) throws Exception {
    // 创建ConnectionFactory
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    // 设置虚拟主机
    connectionFactory.setVirtualHost("/");
    // 创建Connection
    Connection connection = connectionFactory.newConnection();
    // 创建Channel
    Channel channel = connection.createChannel();
    // 交换机名称
    String exchangeName = "test_topic_exchange";
    // routing key
    String routingKey1 = "user.topic.save";
    String routingKey2 = "user.topic.update";
    String routingKey3 = "user.topic.del";
    // 发送消息
    String msg = "RabbitMQ Topic Exchange TEST";
    channel.basicPublish(exchangeName, routingKey1, null, msg.getBytes());
    channel.basicPublish(exchangeName, routingKey2, null, msg.getBytes());
    channel.basicPublish(exchangeName, routingKey3, null, msg.getBytes());
}

消费者

public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setAutomaticRecoveryEnabled(true);
    connectionFactory.setNetworkRecoveryInterval(3000);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    // 交换机名称
    String exchangeName = "test_topic_exchange";
    // 交换机类型
    String exchangeType = "topic";
    // 消息队列名称
    String queueName = "test_topic_queue";
    // 配置所有以user.开头的消息
    String routingKey = "user.#";
    // 声明交换机
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    // 表示一个队列
    channel.queueDeclare(queueName, false, false, false, null);
    // 建立Exchange和Queue绑定关系
    channel.queueBind(queueName, exchangeName, routingKey);
    // 参数说明:队列名称、是否自动ACK、DefaultConsumer(消费者)
    channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("收到消息:" + new String(body));
        }
    });
}

五、Headers Exchange:头交换机

5.1 基础

介绍

类似直连交换机,但是头交换机使用多个消息属性代替路由键建立路由规则。通过判断消息头的值能否与指定的绑定相匹配来确立路由规则。

通过匹配AMQP消息的header而非路由键。性能方面比后者查很多,所以在实际项目中用的很少。

此交换机有个重要参数:”x-match”

  • 当”x-match”为“any”时,消息头的任意一个值被匹配就可以满足条件。

  • 当”x-match”设置为“all”时,就需要消息头的所有值都匹配成功。

图示

5.2 代码 

5.2.1 生产者

代码

public static void main(String[] args) throws Exception {
        // 创建ConnectionFactory
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        // 设置虚拟主机
        connectionFactory.setVirtualHost("/");
        // 创建Connection
        Connection connection = connectionFactory.newConnection();
        // 创建Channel
        Channel channel = connection.createChannel();
        // 交换机名称
        String exchangeName = "test_headers_exchange";
        // 消息头
        Map<String, Object> headers = new HashMap<>();
        headers.put("format", "JSON");
        headers.put("type", "pdf");
        // 添加到headers
        AMQP.BasicProperties basicProperties = new AMQP.BasicProperties.Builder()
                .headers(headers)
                .build();
        // routing key
        // 发送5条消息
        for (int i = 1; i <= 5; i++) {
            String msg = "RabbitMQ headers Exchange  TEST" + i;
            channel.basicPublish(exchangeName, "", basicProperties, msg.getBytes());
        }
        channel.close();
        connection.close();
    }

5.2.2 消费者

前提

声明消息队列时,需要指定header参数,其中x-match为特殊的headers

  • all时则表示要匹配所有的header

  • any时则表示只要匹配其中的一个header即可

例如:

Map<String, Object> arguments = new HashMap<>();
arguments.put("x-match","all");
// arguments.put("x-match","any");
// 声明一个队列
channel.queueDeclare(queueName,false,false,false,arguments);

代码

public static void main(String[] args) throws Exception {
    ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setHost("127.0.0.1");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("/");
    connectionFactory.setUsername("guest");
    connectionFactory.setPassword("guest");
    connectionFactory.setAutomaticRecoveryEnabled(true);
    connectionFactory.setNetworkRecoveryInterval(3000);
    Connection connection = connectionFactory.newConnection();
    Channel channel = connection.createChannel();
    // 交换机名称
    String exchangeName = "test_headers_exchange";
    // 交换机类型
    String exchangeType = "headers";
    // 消息队列名称
    String queueName = "test_fanout_queue";
    // fanout模式不以routing key为匹配规则,可以为空。
    String routingKey = " ";
    // 声明交换机
    channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
    // 队列的参数
    // x-match为特殊的headers,为all时则表示要匹配所有的header,如果为any则表示只要匹配其中的一个header即可。
    Map<String, Object> arguments = new HashMap<>();
    arguments.put("x-match", "all");
    // arguments.put("x-match","any");
    // 声明一个队列
    channel.queueDeclare(queueName, false, false, false, arguments);
    // 建立Exchange和Queue绑定关系
    channel.queueBind(queueName, exchangeName, routingKey);
    // 参数说明:队列名称、是否自动ACK、DefaultConsumer(消费者)
    channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            System.out.println("收到消息:" + new String(body));
        }
    });
}

六、Default Exchange:默认交换机

6.1 基础

介绍

默认交换机(default exchange)实际上是一个由RabbitMQ预先声明好的名字为空字符串的直连交换机(direct exchange)

它有一个特殊的属性使得它对于简单应用特别有用处:那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(binding key)名称与队列名称相同。

流程

  • 一般情况可以使用RabbitMQ自带默认的Exchange:" "(该Exchange的名字为空字符串,称其为default exchange

  • 这种模式下不需要将Exchange进行任何绑定(binding)操作

  • 消息传递时需要一个“Routing Key”,可以简单的理解为要发送到的队列名字。

  • 如果vhost中不存在Routing Key中指定的队列名,则该消息会被抛弃。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值