RabbitMQ学习笔记[6]-RabbitMQ消息模型-Routing订阅模型Direct与Topic

一、Routing之订阅模型-Direct

We were using a fanout exchange, which doesn't give us much flexibility - it's only capable of mindless broadcasting.

We will use a direct exchange instead. The routing algorithm behind a direct exchange is simple - a message goes to the queues whose binding key exactly matches the routing key of the message.

我们使用的是 fanout 交换机,这种交换并没有给我们带来太大的灵活性-它只能进行无意识的广播。

我们将使用 direct 交换机。direct 交换背后的路由算法很简单-一条消息进入binding key与该消息的routing key完全匹配的队列 。

在Fanout模型中,一条消息,会被所有订阅的队列消费。但是在某些场景下,我们希望不同的消息被不同的队列消费,这是就要用到Direct类型Exchange。

在Direct模型下:

  • 队列与交换机的绑定,不能是任意绑定,而是执行一个routingKey(路由key)
  • 消息的生产者在向exchange发送消息时,也需要指定消息的routingKey
  • exchange不再把消息交给每一个绑定的队列,而是根据消息的routingKey进项判断,只有队列的routingKey与消息的routingKey完全一致,才会接收到消息。

图解:

  1. P:生产者,向exchange发送消息,发送消息时,会指定routingKey
  2. X:Exchange(交换机),接受生产者的消息,然后把消息递交给routingKey完全匹配的队列
  3. C1:消费者,其所在的队列指定了需要routingKey为error的消息
  4. C2:消费者,其所在的队列指定了需要routingKey为info、error、warning的消息

1. 开发生产者

package direct;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import utils.RabbitMQUtil;

import java.io.IOException;

public class Provider {

    public static void main(String[] args) throws IOException {
        // 获取连接对象
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = RabbitMQUtil.getChannel(connection);
        if (null == channel) return;

        // 声明fanout交换机
        // 参数1 exchange:交换机名称
        // 参数2 type:交换机类型 direct直连类型
        channel.exchangeDeclare("my_exchange_direct", "direct");

        // 发送消息
        // 参数1 exchange:交换机名称
        // 参数2 routingKey:路由,如果存在消息将以指定的名称路由到队列
        // 参数3 props:消息额外参数
        // 参数4 消息内容
        String routingKey = "routing_key1";
        channel.basicPublish("my_exchange_direct", routingKey, null, ("hello rabbitmq direct [" + routingKey + "] !!!").getBytes());

        // 关闭连接释放资源
        RabbitMQUtil.close(channel, connection);
    }

}

运行后查看web管理端

2. 开发消费者

消费者-one,指定需要routingKey为key1的消息

        // 获取连接对象
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = RabbitMQUtil.getChannel(connection);
        if (null == channel) return;

        // 声明direct交换机
        // 参数1 exchange:交换机名称
        // 参数2 type:交换机类型 direct类型
        channel.exchangeDeclare("my_exchange_direct", "direct");

        // 创建临时队列
        String queueName = channel.queueDeclare().getQueue();
        // routingKey绑定队列和交换机
        channel.queueBind(queueName, "my_exchange_direct", "routing_key1");

        // 消费消息
        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("消费者--one--" + new String(body));
            }
        });

消费者-two,指定需要routingKey为key1、key2、key3的消息

        // 获取连接对象
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = RabbitMQUtil.getChannel(connection);
        if (null == channel) return;

        // 声明direct交换机
        // 参数1 exchange:交换机名称
        // 参数2 type:交换机类型 direct类型
        channel.exchangeDeclare("my_exchange_direct", "direct");

        // 创建临时队列
        String queueName = channel.queueDeclare().getQueue();
        // routingKey绑定队列和交换机
        channel.queueBind(queueName, "my_exchange_direct", "routing_key1");
        channel.queueBind(queueName, "my_exchange_direct", "routing_key2");
        channel.queueBind(queueName, "my_exchange_direct", "routing_key3");

        // 消费消息
        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("消费者--two--" + new String(body));
            }
        });

routingKey为key1,测试:

此时两个消费者都接收到了routing_key1的消息。

更改routingKey为key2,测试

二、Routing之订阅模型-Topic

Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定RoutingKey的时候使用通配符,这种模型RoutingKey一般都是由一个或者多个单词组成,多个单词之间以'.'分割,例如:log.debug

Although using the direct exchange improved our system, it still has limitations - it can't do routing based on multiple criteria.

In our logging system we might want to subscribe to not only logs based on severity, but also based on the source which emitted the log. You might know this concept from the syslog unix tool, which routes logs based on both severity (info/warn/crit...) and

facility (auth/cron/kern...).

That would give us a lot of flexibility - we may want to listen to just critical errors coming from 'cron' but also all logs from 'kern'.

To implement that in our logging system we need to learn about a more complex topic exchange.

尽管使用Direct交换对我们的系统进行了改进,但它仍然存在局限性-它无法基于多个条件进行路由。

在我们的日志记录系统中,我们可能不仅要根据严重性订阅日志,还要根据发出日志的源订阅日志。您可能从syslog unix工具中了解了这个概念,该 工具根据严重性(info / warn / crit ...)和工具(auth / cron / kern ...)路由日志。

这将给我们带来很大的灵活性-我们可能只想听听来自'cron'的严重错误,也可以听听'kern'的所有日志。

为了在我们的日志系统中实现这一点,我们需要了解更复杂的Topic交换。

  • * (star) can substitute for exactly one word.       // (星号)可以代替一个单词。
  • # (hash) can substitute for zero or more words. // (哈希)可以替代零个或多个单词。

如:

  • log.# 匹配log.list.save或者log.list等
  • log.* 只匹配log.list

1. 开发生产者

package topic;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import utils.RabbitMQUtil;

import java.io.IOException;

public class Provider {

    public static void main(String[] args) throws IOException {
        // 获取连接对象
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = RabbitMQUtil.getChannel(connection);
        if (null == channel) return;

        // 声明fanout交换机
        // 参数1 exchange:交换机名称
        // 参数2 type:交换机类型 topic主题类型
        channel.exchangeDeclare("my_exchange_topic", "topic");

        // 发送消息
        // 参数1 exchange:交换机名称
        // 参数2 routingKey:路由,如果存在消息将以指定的名称路由到队列
        // 参数3 props:消息额外参数
        // 参数4 消息内容
        String routingKey = "routing.project";
        channel.basicPublish("my_exchange_topic", routingKey, null, ("hello rabbitmq direct [" + routingKey + "] !!!").getBytes());

        // 关闭连接释放资源
        RabbitMQUtil.close(channel, connection);
    }

}

运行,web管理端

2. 开发消费者

消费者-one routingKey使用*通配符方式

        // 获取连接对象
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = RabbitMQUtil.getChannel(connection);
        if (null == channel) return;

        // 声明direct交换机
        // 参数1 exchange:交换机名称
        // 参数2 type:交换机类型 topic类型
        channel.exchangeDeclare("my_exchange_topic", "topic");

        // 创建临时队列
        String queueName = channel.queueDeclare().getQueue();
        // routingKey绑定队列和交换机
        channel.queueBind(queueName, "my_exchange_topic", "routing.*");

        // 消费消息
        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("消费者--one--" + new String(body));
            }
        });

消费者-two routingKey使用#通配符方式

        // 获取连接对象
        Connection connection = RabbitMQUtil.getConnection();
        Channel channel = RabbitMQUtil.getChannel(connection);
        if (null == channel) return;

        // 声明direct交换机
        // 参数1 exchange:交换机名称
        // 参数2 type:交换机类型 topic类型
        channel.exchangeDeclare("my_exchange_topic", "topic");

        // 创建临时队列
        String queueName = channel.queueDeclare().getQueue();
        // routingKey绑定队列和交换机
        channel.queueBind(queueName, "my_exchange_topic", "routing.#");

        // 消费消息
        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("消费者--two--" + new String(body));
            }
        });

运行,测试结果:

修改routingKey为 routing.project.list,测试:

同理将routingKey改为 *.routing.* ,或者 *.routing.#,原理都是一样的。

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值