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监听处理。
这里写图片描述

猜你喜欢

转载自blog.csdn.net/hetoby/article/details/79873879
今日推荐