Rabbit MQ - tutorial 4 Routing, receiving messages selectively

本例在前一例的基礎上,通過Routing過濾消息,只接收感興趣的message。

基本概念

  • binding: A binding is a relationship between an exchange and a queue.
  • binding key: exchange和queue綁定時,用於指定哪些消息要被放到該queue中。
  • routing key: 發佈的消息的路由,用於和binding key匹配,決定放入哪個queue.
  • 多重綁定: 不同的queue可以使用相同的binding
    key來綁定到同一個exchange;效果和fanout類型的exchange相同。

Tutorial 4 - 消息路由routing

(1) Producer

本例Producer部分,隨機生成severity=info, warning或者error的log(message),創建名爲“direct_logs”的exchange,併發布routing key爲severity的消息到RabbitMQ server,服務器收到消息之後就把消息丟給exchange,後面就由exchange決定消息被丟棄還是被放到某個queue中。

//MyMessage.java
public class MyMessage {
    private String severity;
    private String message;

    public MyMessage(){
        this.severity = "info";
        this.message = "this is an info message";
    }

    public String getSeverity() {
        return severity;
    }

    public void setSeverity(String severity) {
        this.severity = severity;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
//MyTask.java
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.util.Random;

//producer發送消息給exchange,exchange決定把消息放到哪個queue裏面
public class MyTask implements Runnable {
    //message index,用來查看多個consumer同時從broker取message時,消息是怎麼被分派的
    private static Integer index = 0;

    //define name of the exchange
    private static final String EXCHANGE_NAME = "direct_logs";
    //connection to the server(broker)
    private Connection rbtMqConn;
    //channel
    private Channel rbtMqChnl;

    //控制停止線程
    private boolean isStop = false;
    public void setIsStop(boolean stop){
        this.isStop = stop;
    }

    @Override
    public void run() {

        try{
            //1.創建一個connection鏈接到RabbitMQ服務器(connection爲我們處理socket/協議/認證授權等)
            ConnectionFactory factory = new ConnectionFactory();
            //本例使用本機作爲服務器;Consumer也從這個broker接收消息,也可以使用其它主機,比如172.16.21.111
            factory.setHost("localhost");
            rbtMqConn = factory.newConnection();

            //2.創建一個channel
            rbtMqChnl = rbtMqConn.createChannel();

            //3.聲明exchange,名字爲EXCHANGE_NAME,類型爲direct
            //這一步必須,因爲不能發佈到不存在的exchange是不允許的
            //通過rabbitmqctl list_exchanges -p <vhost>,查看vhost上的exchange
            rbtMqChnl.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

            //如果沒有queue綁定到exchange上,這些消息將會丟失,但這對本例來說沒問題;
            //如果沒有consumer正在監聽,我們可以放心地丟棄消息。

            //send message per 3s
            while (!isStop){
                //隨機生成不同severity的log(message)
                MyMessage myMessage = getMessage(index++);
                //4.通過指定的exchange發佈routingKey爲severity(info/warning/error)的消息:
                rbtMqChnl.basicPublish(EXCHANGE_NAME,
                         myMessage.getSeverity()/*routing key*/,
                         null,
                         myMessage.getMessage().getBytes("UTF-8"));
                System.out.println(" [Producer] Sent '" + myMessage.getSeverity() + ":" + myMessage.getMessage() + "'");
                Thread.sleep(3000);
            }

            //5.最後,使用完之後,關閉channel和connection;
            rbtMqChnl.close();
            rbtMqConn.close();
        }catch(Exception ex){
            System.out.println(ex.getMessage());
        }

        System.out.println(" [Producer] Send task stop");
    }

    /**
     * 生成隨機消息用於發送給RabbitMQ服務器
     * */
    private static MyMessage getMessage(Integer index) {
        MyMessage msg = new MyMessage();

        StringBuilder dotMsg = new StringBuilder("");
        dotMsg.append(String.format("[%d]", index));

        Random rand = new Random();
        Integer num = rand.nextInt(6) + 1;//[1, 6]
        for(Integer i=1; i <= num; ++i){
            dotMsg.append(".").append(i.toString());
        }

        msg.setMessage(dotMsg.toString());

        int type = num % 3;
        switch (type){
            case 0:
                msg.setSeverity("info");
                break;
            case 1:
                msg.setSeverity("warning");
                break;
            case 2:
                msg.setSeverity("error");
                break;
        }

        return msg;
    }

}
//MyProducer.java
public class MyProducer {
    public static void main(String[] argv) throws Exception {
        MyTask sendTask = new MyTask();
        Thread thread = new Thread(sendTask);
        thread.start();
        //let the thread run 60 seconds
        Thread.sleep(60000);
        sendTask.setIsStop(true);
    }
}

(2) Consumer

本例Consumer部分,生成了一個隨機queue,然後分別使用不同的bindingkey(一個爲warning,另一個爲error)綁定到名爲“direct_logs”的exchange,監聽符合條件的message。

//MyConsumer.java
import com.rabbitmq.client.*;
import java.io.IOException;

public class MyConsumer
{
    private static final String EXCHANGE_NAME = "direct_logs";

    public static void main(String[] argv) throws Exception {
        //1.創建connection鏈接到RabbitMQ Server
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");//使用本機的RabbitMQ Server
        Connection connection = factory.newConnection();

        //2.創建channel
        Channel channel = connection.createChannel();

        //3.聲明exchange,指定類型爲direct
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);

        //4.使用隨機生成的消息隊列
        //注意,當consumer退出連接時,該隨機隊列會自動刪除
        String queueName = channel.queueDeclare().getQueue();
        System.out.println(" [Consumer] declare to get random queue: " + queueName);

        //5.queue綁定到exchange,並指定binding key(也可以稱作routing key)
        //rabbitMQ接收消息之後給exchange,而exchange將根據該路由決定是否將消息放入該消息隊列
        //routing key(binding key)的含義隨exchange的類型的不同而不同;
        //對於fanout類型的exchange來說,routing key是沒有意義的,會被直接忽略。
        //注意:支持多個綁定,見下面代碼
        //使用rabbitmqctl list_bindings命令,查看綁定
        //channel.queueBind(queueName, EXCHANGE_NAME, "info"/*bindingKey*/);
        channel.queueBind(queueName, EXCHANGE_NAME, "warning"/*bindingKey*/);
        channel.queueBind(queueName, EXCHANGE_NAME, "error"/*bindingKey*/);

        //6.設置消息處理函數
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println(" [Consumer] Received '" + envelope.getRoutingKey() + "':'" + message + "'");
            }
        };

        //7.監聽消息
        channel.basicConsume(queueName, true, consumer);
    }
}

(3) 運行結果

只有routing key = warning和error的message被consumer監聽處理。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章