rabbitmq direct 多个消费者_RabbitMQ - 交换器

本文介绍了RabbitMQ中的交换器功能,包括Direct、Fanout和Topic三种类型。Direct交换器根据路由键进行精确匹配;Fanout交换器将消息广播到所有绑定的队列;Topic交换器利用通配符实现灵活的消息过滤。文中通过实例展示了不同交换器的工作原理和使用场景。
摘要由CSDN通过智能技术生成

在Rabbitmq中,消息发送给交换器,交换器根据一定的规则把消息发给队列,broker再把消息发送给消费者,或者发送至主动从队列拉去消息。前面几张讲了队列的相关东西,这篇看看交换器是如何把消息发送给队列的。

b8d688faf002b805c1674456b604f355.png

交换器

交换器接收消息并将其路由到零个或多个队列,它支持四种交换类型: Direct 、 Fanout 、 Topic 、 Headers 。还还声明了一些属性,其中最重要的是:交换器的名称、交换器类型、是否持久化、是否自动删除、参数。

是否持久化,决定了rabbitmq重启后,交换器是否存在。是否自动删除,决定了当最后一个队列被解除绑定时,交换器是否被删除。

Exchange.DeclareOk exchangeDeclare(String exchange,        BuiltinExchangeType type,        boolean durable,        boolean autoDelete,        boolean internal,        Map arguments) throws IOException;

默认的交换器

默认的交换器名称为 "" ,即空字符串。当我们没有定义的时候,看起来就像消息直接发送到队列一样。

Direct

根据消息路由键将消息传递到队列。主要的步骤如下:

  1. 通过路由键K把队列绑定到交换器
  2. 当带有路由键R的新消息到达交换器时,如果K = R,交换器将其路由到队列
c6d900998c607ebe836efe99efa5b686.png

生产者代码,通过 channel.basicPublish 把带有路由键("images.archive", "images.crop", "images.resizer")的消息发送给交换器images。

public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    // 创建一个Channel    try (Connection connection = factory.newConnection();         Channel channel = connection.createChannel()) {        // 定义交换器        channel.exchangeDeclare(Constant.EXCHANGE_NAME, BuiltinExchangeType.DIRECT);        String[] routingKeys = {"images.archive", "images.crop", "images.resizer"};        for (int i = 0; i < routingKeys.length; i++) {            // 把消息发送到队列中            channel.basicPublish(Constant.EXCHANGE_NAME, routingKeys[i], null, routingKeys[i].getBytes());            channel.basicPublish(Constant.EXCHANGE_NAME, routingKeys[i], null, routingKeys[i].getBytes());        }        System.out.println("Sent complete");    }}

ArchiveRec1消费者,通过 channel.queueBind 把交换器images、路由键images.archive、队列archive1绑定一起。

// 定义队列的名称public final static String QUEUE_NAME = "archive1";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_NAME, "images.archive");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("ArchiveRec1 Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

ArchiveRec1消费者,通过 channel.queueBind 把交换器images、路由键images.archive、队列archive2绑定一起。除了队列名称,其他与上面代码雷同,就不贴了。

CropRec消费者,通过 channel.queueBind 把交换器images、路由键images.crop、队列cropper绑定一起。

// 定义队列的名称public final static String QUEUE_NAME = "cropper";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_NAME, "images.crop");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("cropper Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

运行ArchiveRec1、ArchiveRec2各一次,运行CropRec两次。启动消费者后,再运行生产者DirectExchange。

运行结果如下,ArchiveRec1和ArchiveRec2各消费2条数据,两个CropRec一共消费2条数据,说明同一个队列两个消费者,他们是轮询消费的。

ArchiveRec1

e52e60c0e3fbbdd164b644a3033d000e.png

ArchiveRec2

e3ede9e2e12522e6219f5f197a26dc0f.png

CropRec

fb62487e85a56172b485f63651cdd3c7.png

fanout

把消息发送给交换器所有的队列上,忽略的路由键的影响。也就是说,当多个队列跟这个交换器绑定的时候,交换器每收到一条消息,就会群发给这些队列。虽然Direct上面的例子中,也可以通过多个队列和路由键交换器绑定,达到部分群发的功能,但是fanout对于群发的功能还是更方便些。

bed183498f8800204cc29a87b8c12149.png

照着direct的例子改一下,把交换器、队列名称改一下,再把交换类型改为fanout。

ArchiveRec1代码如下,ArchiveRec2雷同。

// 定义队列的名称public final static String QUEUE_NAME = "fanout_archive1";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_FANOUT_NAME, "images.archive");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("ArchiveRec1 Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

CropRec代码如下:

// 定义队列的名称public final static String QUEUE_NAME = "fanout_cropper";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_FANOUT_NAME, "images.crop");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("cropper Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

运行ArchiveRec1、ArchiveRec2各一次,运行CropRec两次。启动消费者后,再运行生产者FanoutExchange。

运行结果如下,ArchiveRec1和ArchiveRec2各消费6条数据,其他路由键的信息都收到了。两个CropRec一共消费6条数据,fanout_cropper队列也收到了6条信息,被两个消费者分别消费3条。

ArchiveRec1

343ab8be6259071f751a702d2b27790a.png

ArchiveRec2

6ba23efd163c7204abd35510085e354c.png

CropRec

a4f01ae9a24a1d56aa68e6036b64d9d4.png

Topic

通过通配符,来消费所要的消息。名称和activemq的发布订阅一样,但是特性和activemq的通配符差不多。

通配符有 # 和 * 。 # 是匹配一个或多个, * 是匹配一个。

AllRec用来接收images开头所有的消息,ArchiveRec用来接收images.archive开头所有的消息,ARec用来接收images开头a结尾的消息,CropRec用来接收images.cropRec开头所有的消息。

生产者:

public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    // 创建一个Channel    try (Connection connection = factory.newConnection();         Channel channel = connection.createChannel()) {        // 定义交换器        channel.exchangeDeclare(Constant.EXCHANGE_TOPIC_NAME, BuiltinExchangeType.TOPIC);        String[] routingKeys = {"images.archive.a", "images.archive.b", "images.crop.a"};        for (int i = 0; i < routingKeys.length; i++) {            // 把消息发送到队列中            channel.basicPublish(Constant.EXCHANGE_TOPIC_NAME, routingKeys[i], null, routingKeys[i].getBytes());        }        System.out.println("Sent complete");    }}

AllRec

// 定义队列的名称public final static String QUEUE_NAME = "all";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_TOPIC_NAME, "images.#");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("AllRec Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

ArchiveRec

// 定义队列的名称public final static String QUEUE_NAME = "archive";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_TOPIC_NAME, "images.archive.*");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("ArchiveRec Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

ARec

// 定义队列的名称public final static String QUEUE_NAME = "a";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_TOPIC_NAME, "images.*.a");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("ARec Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

CropRec

// 定义队列的名称public final static String QUEUE_NAME = "crop";public static void main(String[] args) throws IOException, TimeoutException {    // 声明一个连接工厂    ConnectionFactory factory = new ConnectionFactory();    // 创建一个与rabbitmq服务器的连接    Connection connection = factory.newConnection();    // 创建一个Channel    Channel channel = connection.createChannel();    // 通过Channel定义队列    // 这边声明队列,是因为可能在发布服务器之前启动消费者,所以在尝试使用队列中的消息之前确保队列存在    channel.queueDeclare(QUEUE_NAME, false, false, false, null);    // 绑定交换器、路由键、队列    channel.queueBind(QUEUE_NAME, Constant.EXCHANGE_TOPIC_NAME, "images.crop.*");    System.out.println("Waiting for messages.");    // 异步回调处理    DeliverCallback deliverCallback = (consumerTag, delivery) -> {        String message = new String(delivery.getBody(), "UTF-8");        System.out.println("CropRec Received '" + message + "'");    };    // 接收消息    channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {    });}

运行结果如下:

AllRec收到了全部消息

e0ae46377abe7996f211202c4c407224.png

ArchiveRec收到了2条images.archive开头的消息

f90ae1045d3bcb093714fe2bdb3491af.png

ARec收到了2条images开头a结尾的消息

9b669eb71803d7bf4df1e123ed7cc219.png

CropRec收到了1条images.crop开头的消息

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值