Common mode switch summarized RabbitMQ

I. Introduction

RabbitMQ The switch has Direct Exchange (direction switch), Topic Exchange (wildcard switch), Fanout Exchange (broadcast switches), Headers Exchange (Headers switch) are four commonly used on the first three, we will be the first three make a brief summary, a deeper understanding of MQ in the switch mechanism.

Two, Fanout Exchange (broadcast switch)

[A] FIG. Model: This model is similar to a broadcast mode, all messages sent to the Fanout Exchange switch, will be sent to all queues bound to the above switches, so that bind to these queues to the consumer you can receive the message.

[B] Description:

  • This mode need not specify routing Routing key button, a switch can queue bind multiple queues, one queue may simultaneously be bound to a plurality of exchange switches;
  • If the message is sent to the switch, but the switch is not bound to the above queue, then the message will be discarded;

[C] Manufacturer:

/**
 * @Description: 广播式交换机生产者
 * @Author: weixiaohuai
 * @Date: 2019/6/25
 * @Time: 20:43
 * <p>
 * 说明:
 * 1. 广播式交换机发送的消息的时候不需要指定routing key路由键;
 * 2. 所有发送到广播式交换机上面的消息都会被发送到与之绑定的所有队列上;
 * 3.
 */
public class Producer {
    private static final String EXCHANGE_NAME = "fanout_exchange";
    //广播式交换机
    private static final String EXCHANGE_TYPE = "fanout";

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建交换机对象
            channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
            //发送消息到交换机exchange上
            String msg = "hello fanout exchange!!";
            channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

[D] Consumer 1:

public class Consumer01 {
    private static Logger logger = LoggerFactory.getLogger(Consumer01.class);
    private static final String QUEUE_NAME = "fanout_exchange_queue01";
    private static final String EXCHANGE_NAME = "fanout_exchange";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, StandardCharsets.UTF_8);
                    logger.info("【Consumer01】receive message: " + message);
                }
            };
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

[E] Consumer 2:

public class Consumer02 {
    private static Logger logger = LoggerFactory.getLogger(Consumer02.class);
    private static final String QUEUE_NAME = "fanout_exchange_queue02";
    private static final String EXCHANGE_NAME = "fanout_exchange";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, StandardCharsets.UTF_8);
                    logger.info("【Consumer02】receive message: " + message);
                }
            };
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

[F] operating results:

1 and 2 because consumers are consumer and bound queue fanout_exchange_queue01 fanout_exchange_queue02, and this gave two queues bound to fanout_exchange switch above all two consumers can receive this message.

[G] MQ management console switch binding message

 

Three, Direct Exchange (direction switch)

[A] FIG Model:

[B] Description:

  • Direct Exchange to send any message will be forwarded to the specified queue Queue RouteKey specified;
  • When the production of the producer need to perform message routing Routing Key bond;
  • When the switch queue bindings need to specify the Binding Key, only routing keys and key bindings same words, in order to send a message to bind the queue of consumers;
  • If the queue name does not exist RouteKey vhost specified, then the message is discarded;

[C] Manufacturer:

/**
 * @Description: Direct Exchange直连交换机
 * @Author: weixiaohuai
 * @Date: 2019/6/25
 * @Time: 21:22
 * <p>
 * 说明:
 * 1. 任何发送到Direct Exchange的消息都会被转发到指定RouteKey中指定的队列Queue;
 * 2. 生产者生产消息的时候需要执行Routing Key路由键;
 * 3. 队列绑定交换机的时候需要指定Binding Key,只有路由键与绑定键相同的话,才能将消息发送到绑定这个队列的消费者;
 * 4. 如果vhost中不存在RouteKey中指定的队列名,则该消息会被丢弃;
 */
public class CustomProducer {
    private static final String EXCHANGE_NAME = "direct_exchange";
    //交换机类型:direct
    private static final String EXCHANGE_TYPE = "direct";
    //路由键
    private static final String EXCHANGE_ROUTE_KEY = "user.add";

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建交换机对象
            channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
            //发送消息到交换机exchange上
            String msg = "hello direct exchange!!!";
            //指定routing key为info
            channel.basicPublish(EXCHANGE_NAME, EXCHANGE_ROUTE_KEY, null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

[D] Consumer 1:

public class Consumer01 {
    private static Logger logger = LoggerFactory.getLogger(Consumer01.class);
    private static final String QUEUE_NAME = "direct_exchange_queue01";
    private static final String EXCHANGE_NAME = "direct_exchange";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY = "user.delete";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY);

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, StandardCharsets.UTF_8);
                    logger.info("【Consumer01】receive message: " + message);
                }
            };
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

[E] Consumer 2:

public class Consumer02 {
    private static Logger logger = LoggerFactory.getLogger(Consumer02.class);
    private static final String QUEUE_NAME = "direct_exchange_queue02";
    private static final String EXCHANGE_NAME = "direct_exchange";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY01 = "user.add";
    private static final String EXCHANGE_ROUTE_KEY02 = "user.delete";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY01);
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY02);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, StandardCharsets.UTF_8);
                    logger.info("【Consumer02】receive message: " + message);
                }
            };
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

[F] operating results:

因为生产者生产消息指定的路由键为user.add,而且消费者1绑定的队列direct_exchange_queue01对应的绑定键为user.delete,显然消费者1接收不了该消息,而消费者2指定的绑定键为user.add和user.delete,显然消费者就能成功消费此消息。

【g】MQ管理控制台交换机绑定消息

 

四、Topic Exchange(通配符交换机)

【a】模型图:

【b】说明:

  • 任何发送到Topic Exchange的消息都会被转发到所有满足Route Key与Binding Key模糊匹配的队列Queue上;
  • 生产者发送消息的时候需要指定Route Key,同时绑定Exchange与Queue的时候也需要指定Binding Key;
  • #” 表示0个或多个关键字,“*”表示匹配一个关键字;
  • 如果Exchange没有发现能够与RouteKey模糊匹配的队列Queue,则会抛弃此消息;
  • 如果Binding中的Routing key *,#都没有,则路由键跟绑定键相等的时候才转发消息,类似Direct Exchange;如果Binding中的Routing key为#或者#.#,则全部转发,类似Fanout Exchange;

【c】生产者:

/**
 * @Description: Topic Exchange通配符交换机
 * @Author: weixiaohuai
 * @Date: 2019/6/25
 * @Time: 21:22
 * <p>
 * 说明:
 * 1. 任何发送到Topic Exchange的消息都会被转发到所有满足Route Key与Binding Key模糊匹配的队列Queue上;
 * 2. 生产者发送消息的时候需要指定Route Key,同时绑定Exchange与Queue的时候也需要指定Binding Key;
 * 3. #” 表示0个或多个关键字,“*”表示匹配一个关键字;
 * 4. 如果Exchange没有发现能够与RouteKey模糊匹配的队列Queue,则会抛弃此消息;
 */
public class CustomProducer {
    private static final String EXCHANGE_NAME = "topic_exchange";
    //交换机类型:direct
    private static final String EXCHANGE_TYPE = "topic";
    //路由键
    private static final String EXCHANGE_ROUTE_KEY = "user.add.submit";

    public static void main(String[] args) {
        //获取MQ连接
        Connection connection = MQConnecitonUtils.getConnection();
        //从连接中获取Channel通道对象
        Channel channel = null;
        try {
            channel = connection.createChannel();
            //创建交换机对象
            channel.exchangeDeclare(EXCHANGE_NAME, EXCHANGE_TYPE);
            //发送消息到交换机exchange上
            String msg = "hello topic exchange!!!";
            //指定routing key为info
            channel.basicPublish(EXCHANGE_NAME, EXCHANGE_ROUTE_KEY, null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != channel) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if (null != connection) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【d】消费者1:

public class Consumer01 {
    private static Logger logger = LoggerFactory.getLogger(Consumer01.class);
    private static final String QUEUE_NAME = "direct_exchange_queue01";
    private static final String EXCHANGE_NAME = "topic_exchange";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY = "user.#";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY);

            channel.basicQos(1);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, StandardCharsets.UTF_8);
                    logger.info("【Consumer01】receive message: " + message);
                }
            };
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

【e】消费者2:

public class Consumer02 {
    private static Logger logger = LoggerFactory.getLogger(Consumer02.class);
    private static final String QUEUE_NAME = "direct_exchange_queue02";
    private static final String EXCHANGE_NAME = "topic_exchange";
    //binding key
    private static final String EXCHANGE_ROUTE_KEY01 = "user.*";

    public static void main(String[] args) {
        //获取MQ连接对象
        Connection connection = MQConnecitonUtils.getConnection();
        try {
            //创建消息通道对象
            final Channel channel = connection.createChannel();
            //创建队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            //将队列绑定到交换机上,并且指定routing_key
            channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, EXCHANGE_ROUTE_KEY01);

            //创建消费者对象
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    //消息消费者获取消息
                    String message = new String(body, StandardCharsets.UTF_8);
                    logger.info("【Consumer02】receive message: " + message);
                }
            };
            //监听消息队列
            channel.basicConsume(QUEUE_NAME, true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

【f】运行结果:

因为生产者发送消息的时候指定了Routing Key为user.add.submit, 而消费者1所在的队列Binding Key为user.#, #能够匹配一个或者多个,所有消费者1能够消费此消息; 但是消费者2指定的Binding Key为user.*, *只能匹配一个,所有并不能够匹配到user.add.submit这个路由键,所以消费者2不能消费此消息。

【g】MQ管理控制台交换机绑定消息

 

五、总结

需要注意的是,RabbitMQ中还有一个默认交换机(Default Exchange):
【a】:默认的Exchange不能进行绑定操作;
【b】:任何发送到默认交换机的消息都会被转发到路由键Routing key和队列queue名字相同的Queue中;
【c】:如果vhost中不存在Routing key中指定的队列名,则该消息会被抛弃;
【d】:该种方式类似于fanout exchange广播式交换机;

本文通过一些简单的示例并且以画图的方式总结了RabbitMQ中常用的三种交换机:直连交换机、通配符交换机以及广播式交换机,相对来说,广播式交换机的效率较高一些,其次是直连交换机,通配符交换机由于要匹配路由键与绑定键的关系,效率相对较低一点点。

 

 

Guess you like

Origin blog.csdn.net/Weixiaohuai/article/details/93652637