Message queue RabbitMQ core: switch (routing, topic, publish and subscribe)

insert image description here


Previous article : Message queue RabbitMQ core: simple (Hello World) mode, queue (Work Queues) mode, release confirmation mode


In the previous study, a work queue was created using . We assume that behind the work queue, each task is handed over to exactly one consumer (worker process). In the past, messages were sent to the queue and then consumed by consumers. In fact, there is a default switch in RabbitMQ, and there is no need to specify a switch when sending messages.

insert image description here

In the previous scenario, when the producer sends a message, the message can only be received by one consumer at this time, and each consumer is in a competitive relationship. The corresponding architecture diagram:

insert image description here

So, is it possible for a producer to send a message, and a message can be received by multiple consumers?

The answer is yes. At this point, the concept of a switch must be introduced. The architecture diagram above evolves as follows:

insert image description here

1. Overview of the switch

The core idea of ​​the RabbitMQ messaging model is that messages produced by producers are never sent directly to queues . In fact, often the producer doesn't even know which queues these messages are delivered to.

Instead, producers can only send messages to an exchange, and what an exchange does is very simply, it receives messages from producers on the one hand, and pushes them into a queue on the other hand . The switch must know exactly what to do with the messages it receives. Should these messages be put on a specific queue or should they be put on many queues or should they be discarded. This is determined by the type of switch.

There are a total of the following types of switches :

Direct / Routing (direct), Topic (topic), Title (headers), Fanout / Publish Subscribe (fanout)

unnamed switch

Previously the default exchange was used, identified by an empty string ("").

 channel.basicPublish("","hello",null,message.getBytes());

The first parameter is the name of the switch. An empty string means the default or no name switch: the message can be routed to the queue is actually specified by the routingKey(bindingkey)binding key

temporary queue

We need a brand new empty queue every time we connect to Rabbit, for this we can either create a queue with a random name , or even better let the server choose a random queue name for us. Second , once we disconnect the consumer, the queue will be deleted automatically .

The way to create a temporary queue is as follows:

String queueName = channel.queueDeclare().getQueue();

The resulting queue is usually as follows:

insert image description here

bindings

Binding is actually a bridge between exchange and queue , it tells us that exchange has a binding relationship with that queue. For example, the picture below tells us that X is bound to Q1 and Q2
insert image description here

2. Publish and subscribe (fanout)

It broadcasts all messages it receives to all queues it knows about. Default exchange types in the system:

insert image description here

Code combat

insert image description here

Equivalent to binding the same routingKey.

producer:

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 scanner = new Scanner(System.in);
        while (scanner.hasNext()){
    
    
            String message = scanner.next();
            channel.basicPublish(EXCHANGE_NAME,"",null,message.getBytes());
            System.out.println("生产者发送消息:"+message);
        }
    }
}

Consumer 1:

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("ReceiveLogs01接收到的消息:"+new String(message.getBody(),"UTF-8"));
        };
        channel.basicConsume(queueName,true,deliverCallback,consumerTag -> {
    
    });
    }
}

Consumer 2:

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 queueName = channel.queueDeclare().getQueue();

        // 绑定交换机与队列
        channel.queueBind(queueName,EXCHANGE_NAME,"");
        System.out.println("等待接收消息。。。。");

        // 接收消息
        DeliverCallback deliverCallback = (consumerTag, message) ->{
    
    
            System.out.println("ReceiveLogs02接收到的消息:"+new String(message.getBody(),"UTF-8"));
        };
        channel.basicConsume(queueName,true,deliverCallback,consumerTag -> {
    
    });
    }
}

Test it out:
producer sends message

insert image description here
Consumer receives message

insert image description here
insert image description here
Implemented a message to be received by different consumers.

3. Routing (direct)

A queue is only interested in messages from the exchange it is bound to. Binding uses parameters: routingKey to indicate that this parameter can also be called binding key, we use code to create binding channel.queueBind(queueName, EXCHANGE_NAME, "routingKey"); the meaning after binding is determined by its exchange type.

The way the Direct type works is that messages only go to the ones it is bound to. routingKey queue.

insert image description here

In the picture above, we can see that X is bound to two queues, and the binding type is direct. The binding key of queue Q1 is orange, and the binding key of queue Q2 has two: one binding key is black, and the other binding key is green

.In this binding situation, the producer publishes a message to the exchange, and the message whose binding key is orange will be published to the queue Q1. Messages with binding keys blackgreen and will be published to queue Q2, and messages of other message types will be discarded.

multiple binding

If the binding type of the exchange is direct, but the keys of multiple queues it is bound to are the same, in this case, although the binding type is direct, it behaves a bit similar to fanout, just like broadcasting.
insert image description here

Code combat

insert image description here

It is equivalent to binding different routingKeys to send messages to different queues.

Producer:

public class EmitLogDirect {
    
    
    public static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws Exception{
    
    
        Channel channel = RabbitMQUtils.getChannel();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()){
    
    
            String message = scanner.next();
            channel.basicPublish(EXCHANGE_NAME,"info",null,message.getBytes());
            System.out.println("生产者发送消息:"+message);
        }
    }
}

Consumer 1:

public class ReceiveLogsDirect01 {
    
    

    public static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws Exception {
    
    
        Channel channel = RabbitMQUtils.getChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        // 声明队列
        channel.queueDeclare("console", false, false, false, null);
        // 队列绑定交换机  指明 routingKey
        channel.queueBind("console",EXCHANGE_NAME,"info");
        channel.queueBind("console",EXCHANGE_NAME,"warning");

        // 接收消息
        DeliverCallback deliverCallback = (consumerTag, message) ->{
    
    
            System.out.println("ReceiveLogsDirect01接收到的消息:"+new String(message.getBody(),"UTF-8"));
        };
        channel.basicConsume("console",true,deliverCallback,consumerTag -> {
    
    });
    }
}

Consumer 2:

public class ReceiveLogsDirect02 {
    
    
    public static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] args) throws Exception {
    
    
        Channel channel = RabbitMQUtils.getChannel();
        // 声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        // 声明队列
        channel.queueDeclare("disk", false, false, false, null);
        // 队列绑定交换机  指明 routingKey
        channel.queueBind("disk", EXCHANGE_NAME, "error");
        // 接收消息
        DeliverCallback deliverCallback = (consumerTag, message) -> {
    
    
            System.out.println("ReceiveLogsDirect02接收到的消息:" + new String(message.getBody(), "UTF-8"));
        };
        channel.basicConsume("disk", true, deliverCallback, consumerTag -> {
    
    
        });
    }
}

Test it out:
producer sends message

insert image description here
Consumer 1 receives the message

insert image description here
insert image description here

insert image description here
Consumer 2 did not receive the message

insert image description here

The producer sends a message to the queue bound to the specified routingKey, and the corresponding consumer can receive the message.

4. Topic

The routing_key of the message sent to the topic switch cannot be written at will, and must meet certain requirements. It must be a list of words separated by dots . These words can be any words, for example: "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit".this type. Of course this word list cannot exceed 255 bytes at most.

be careful:

  • *(星号)can replace a word
  • #(井号)Can substitute zero or more words

Topic matching case
The binding relationship in the figure below is as follows:
Q1–>binding is a string with orange in the middle and 3 words Q2–>binding is 3 words whose (*.orange.*)
last word is rabbit and the (*.*.rabbit)first word is lazy multiple words(lazy.#)

insert image description here
Notice:

  • When a queue binding key is #, then this queue will receive all data, which is fanouta
  • If there are no # and * in the queue binding key, then directthe

Code combat

producer

public class EmitLogTopic {
    
    
    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);

        Map<String, String> bindingKeyMap = new HashMap<>();
        bindingKeyMap.put("quick.orange.rabbit","被队列 Q1 Q2 接收到");
        bindingKeyMap.put("lazy.orange.elephant","被队列 Q1 Q2 接收到");
        bindingKeyMap.put("quick.orange.fox","被队列 Q1 接收到");
        bindingKeyMap.put("lazy.brown.fox","被队列 Q2 接收到");
        bindingKeyMap.put("lazy.pink.rabbit","虽然满足两个绑定但只被队列 Q2 接收一次");
        bindingKeyMap.put("quick.brown.fox","不匹配任何绑定,不会被任何队列接收到,会被丢弃");
        bindingKeyMap.put("quick.orange.male.rabbit","是四个单词不匹配任何绑定会被丢弃");
        bindingKeyMap.put("lazy.orange.male.rabbit","是四个单词但匹配 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);
        }
    }
}

Consumer 1:

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("ReceiveLogsTopic01接收到的消息:"+new String(message.getBody(),"UTF-8"));
            System.out.println("接收队列"+queueName+"绑定键:"+message.getEnvelope().getRoutingKey());
        };
        channel.basicConsume(queueName,true,deliverCallback,consumerTag -> {
    
    });
    }
}

Consumer 2:

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,"*.*.rabbit");
        channel.queueBind(queueName,EXCHANGE_NAME,"lazy.#");

        System.out.println("等待接收消息。。。。。");

        // 接收消息
        DeliverCallback deliverCallback = (consumerTag, message) ->{
    
    
            System.out.println("ReceiveLogsTopic02接收到的消息:"+new String(message.getBody(),"UTF-8"));
            System.out.println("接收队列"+queueName+"绑定键:"+message.getEnvelope().getRoutingKey());
        };
        channel.basicConsume(queueName,true,deliverCallback,consumerTag -> {
    
    });
    }
}

Test it out:
producer sends message

insert image description here
Consumer 1 receives message
insert image description here
insert image description here
Consumer 2 receives message

insert image description here

insert image description here
According to different routingkey matching rules, the corresponding queue is matched, and the message is successfully consumed.

This is the end of the article shared this time, I hope it can be helpful to everyone.

insert image description here

Guess you like

Origin blog.csdn.net/Zp_insist/article/details/128288651
Recommended