RabbitMQ 使用 | 第四篇:路由选择

RabbitMQ 使用 | 第四篇:路由选择

大部分内容仅仅对官方教程进行了翻译,有些内容为了更简便进行了调整

上一篇说发布/订阅模型的时候讲到了四中Exchange:fanout,direct,topic,header,并且使用了fanout实现了发布订阅模式,在发布/订阅模式中所有的消息都会被订阅者接受。这一节我们会修改发布者和订阅者的代码达到消息过滤的功能,使用的是direct Exchange

Direct Exchange

在开始之前,我们先介绍direct这种Exchange,这种类型的算法很简单,会将消息转发到绑定健和消息发布指定的选择键完全匹配的队列,简单来说,就是我们使用这种Exchange之后,在发送消息的时候可以指定一个key,然后接受订阅也可以指定一个key,如果这两个key相同,消息就会发送到订阅者哪里。

解释绑定健选择键

  • 绑定键

    首先回顾一下上一节绑定的代码:

    channel.queueBind(queueName,EXCHANGE_NAME,"")
    

    绑定表示Exchange和队列之间的关系,我们可以简单的认为:队列对该Exchange的消息感兴趣。

    注意一下第三个参数,我们称之为’绑定健’,这个健依赖Exchange的类型,对于fanout类型,这个值没有意义。
    一个队列可以绑定多个绑定健

  • 选择键

    回顾一下上一节发送消息的代码:

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

    这里的第二个参数就是选择键

实现路由

嗯。理解了绑定键选择键之后,我们可以得出发送端代码:

channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//...
String selectKey = "error";
channel.basicPublish(EXCHANGE_NAME, selectKey, null, message.getBytes());

接收端的代码变成了:

channel.exchangeDeclare(EXCHANGE_NAME, "direct");
String queueName = channel.queueDeclare().getQueue();
String bindKey = "error";
channel.queueBind(queueName, EXCHANGE_NAME, bindKey);

下面是完整的代码:

public class Publisher {
    private static final String EXCHANGE_NAME = "logs";

    public static void main(String[] argv)
            throws java.io.IOException, TimeoutException {

        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        String[] messages = new String[]{"First message.", "Second message..",
                "Third message...", "Fourth message....", "Fifth message....."};
        String selectKey = "error";
        for (String message : messages) {
            channel.basicPublish(EXCHANGE_NAME, selectKey, null, message.getBytes());
            System.out.println("[x] Sent '" + message + "'");
        }
        channel.close();
        connection.close();
    }
}

public class Subscriber {
    private static final String EXCHANGE_NAME = "logs";

    public static void main(String[] argv) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "direct");
        String queueName = channel.queueDeclare().getQueue();
        String bindKey = "error";
        channel.queueBind(queueName, EXCHANGE_NAME, bindKey);

        System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

        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(" [x] Received '" + message + "'");
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }
}

在这里可能会出点问题:因为这里是使用logs作为名称,但是上一篇的例子中已经使用了logs名称了,他的Exchange的类型是fanoutRabbitMQ不允许修改定义。运行会报错,解决办法如下:

打开终端执行以下命令:

rabbitmqctl stop_app

rabbitmqctl reset

rabbitmqctl start_app

控制台输出:

//publisher
[x] Sent 'First message.'
[x] Sent 'Second message..'
[x] Sent 'Third message...'
[x] Sent 'Fourth message....'
[x] Sent 'Fifth message.....'

Process finished with exit code 0

//subscriber
[*] Waiting for messages. To exit press CTRL+C
[x] Received 'First message.'
[x] Received 'Second message..'
[x] Received 'Third message...'
[x] Received 'Fourth message....'
[x] Received 'Fifth message.....'

发送接收没毛病。但是还没有展现出过滤的功能。我们先把接收端的程序停止。
然后在接收端添加一行代码:

 String bindKey = "error";
 System.out.println("接收端启动:我感兴趣的内容是:" + bindKey);

启动.然后修改接收端:

String bindKey = "info";

再启动多一个接收端,此时我们有两个接收端在运行,一个对error感兴趣,一个对info感兴趣。

这个时候我们启动发送端,查看3个控制台:

//Publish
[x] Sent 'First message.'
[x] Sent 'Second message..'
[x] Sent 'Third message...'
[x] Sent 'Fourth message....'
[x] Sent 'Fifth message.....'
Disconnected from the target VM, address: '127.0.0.1:63461', transport: 'socket'

Process finished with exit code 0

//Subscriber
 [*] Waiting for messages. To exit press CTRL+C
 接收端启动:我感兴趣的内容是:error
 [x] Received 'First message.'
 [x] Received 'Second message..'
 [x] Received 'Third message...'
 [x] Received 'Fourth message....'
 [x] Received 'Fifth message.....'

//Subscriber
 [*] Waiting for messages. To exit press CTRL+C
 接收端启动:我感兴趣的内容是:info

根据控制台输出,可以确定消息只发送到了bingKeyerror的订阅者哪里。

接着我们把发送端的selectKey改为info之后,控制台输出:

//Publish
[x] Sent 'First message.'
[x] Sent 'Second message..'
[x] Sent 'Third message...'
[x] Sent 'Fourth message....'
[x] Sent 'Fifth message.....'
Disconnected from the target VM, address: '127.0.0.1:63461', transport: 'socket'

Process finished with exit code 0

//Subscriber
 [*] Waiting for messages. To exit press CTRL+C
 接收端启动:我感兴趣的内容是:error
 [x] Received 'First message.'
 [x] Received 'Second message..'
 [x] Received 'Third message...'
 [x] Received 'Fourth message....'
 [x] Received 'Fifth message.....'

//Subscriber
 [*] Waiting for messages. To exit press CTRL+C
 接收端启动:我感兴趣的内容是:info
 [x] Received 'First message.'
 [x] Received 'Second message..'
 [x] Received 'Third message...'
 [x] Received 'Fourth message....'
 [x] Received 'Fifth message.....'

可以看到,这次启动仅仅bingKeyinfo的订阅者收到消息。

这里介绍了Exchange类型为direct的使用,为了省事,这里顺便把 topic也介绍了。

Topic主题

总的来说topicdirect的使用差不了多少,首先需要把发布端和接收端的Exchange声明改为topic

channel.exchangeDeclare(EXCHANGE_NAME, "topic");

然后发布消息的时候,同样是声明了一个选择键

不通之处在于接收端的绑定健可以是用通配符匹配:

例如:

  • *.info 可以接收选择键a.info b.info等等
  • info.* 可以接收选择键info.a info.b等等
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值