5.主题介绍

主题

Topics(using the java client)

在前面的章节中,我们改进了日志系统。没有使用只能进行虚拟广播的fanout交换机,而是使用了direct交换机,从而获得了选择性接收日志的可能性。
尽管使用direct交换机改进了我们的系统,但它仍然有局限性——它不能基于多个标准进行路由。
在日志系统中,可能不仅希望根据严重程度订阅日志,还希望根据发出日志的源订阅日志。你可能从syslog unix工具中了解到这个概念,该工具可以根据严重程度(info/warn/crit…)和设备(auth/cron/kern…)来路由日志。
这提供了很大的灵活性——我们可能想要监听来自’cron’的严重错误,但也想要监听来自’kern’的所有日志。
要在我们的日志系统中实现它,需要了解一个更复杂的topic交换机。

topic交换机

发送到topic交换机的消息不能有任意的路由键——它必须是由点分隔的单词列表。这些词可以是任何词,但通常它们指明了与信息相关的一些特征。以下是一些有效的路由键示例:stock.usd.nysenyse.vmwquick.orange.rabbit。路由键中可以有任意多的单词,最多为255个字节。
绑定键的形式也必须相同。topic交换机背后的逻辑与direct交换机类似——带有特定路由键的消息将被发送到所有绑定了匹配绑定键的队列。然而,绑定键有两种重要的特殊情况:

  • *可以替换1个单词。
  • #可以替换0个或多个单词。

在示例中解释这一点是最简单的:
在这里插入图片描述
在这个例子中,将发送描述动物的信息。这些消息将与由三个单词(两个点)组成的路由关键字一起发送。路由键中的第一个单词将描述速度,第二个是颜色,第三个是物种:<speed>.<colour>.<species>
我们创建了三个绑定:Q1通过绑定键*.orange.*进行绑定。Q2*.*.rabbitlazy.#绑定。
这些绑定可以总结为:

  • Q1对所有橙色动物感兴趣。
  • Q2想知道关于兔子的一切,还有关于懒惰动物的一切。

路由键为quick.orange.rabbit的消息将被发送到两个队列,lazy.orange.elephant也是。而另一方面,quick.orange.fox只会发送到第一个队列,lazy.brown.fox只会发送到第二个队列。lazy.pink.rabbit只会被发送到第二个队列一次,即使它匹配了两个绑定。quick.brown.fox不匹配任何绑定,所以它将被丢弃。
如果我们违反约定,发送一个或四个单词的消息,比如orange或者quick.orange.male.rabbit会发生什么?这些消息不匹配任何绑定,将会丢失。
另一方面,lazy.orange.male.rabbit即使它有四个单词,也和最后一个绑定相匹配,将会被发送到第二个队列。

topic交换机功能很强大,可以表现得像其他交换机一样工作。
当队列被绑定到#绑定键时,它将接收所有消息,而不管路由键是什么——就像fanout交换机一样。而当特殊字符*#不在绑定中使用时,topic交换机的行为就像是direct交换机一样。

整合代码

我们将在日志系统中使用topic交换机。首先假设日志的路由键有两个单词:<facility>.<severity>
EmitLogTopic.java类的代码:

public class EmitLogTopic {
    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] argv) throws Exception {
        var factory = new ConnectionFactory();
        factory.setHost("192.168.1.254");
        factory.setUsername("admin");
        factory.setPassword("admin123");

        try (var connection = factory.newConnection();
             var channel = connection.createChannel()) {

            channel.exchangeDeclare(EXCHANGE_NAME, "topic");

            var routingKey = getRouting(argv);
            var message = getMessage(argv);

            channel.basicPublish(EXCHANGE_NAME, routingKey, null, 
                    message.getBytes(StandardCharsets.UTF_8));
            System.out.println(" [x] Sent '" + routingKey + "':'" + message + "'");
        }
    }

    private static String getRouting(String[] strings) {
        if (strings.length < 1) {
            return "anonymous.info";
        }
        return strings[0];
    }

    private static String getMessage(String[] strings) {
        if (strings.length < 2) {
            return "Hello World!";
        }
        return joinStrings(strings, " ", 1);
    }

    private static String joinStrings(String[] strings, String delimiter, int startIndex) {
        int length = strings.length;

        if (length == 0) {
            return "";
        }

        if (length < startIndex) {
            return "";
        }

        var words = new StringBuilder(strings[startIndex]);

        for (int i = startIndex + 1; i < length; i++) {
            words.append(delimiter).append(strings[i]);
        }

        return words.toString();
    }
}

ReceiveLogsTopic.java类的代码:

public class ReceiveLogsTopic {
    private static final String EXCHANGE_NAME = "topic_logs";

    public static void main(String[] argv) throws Exception {
        var factory = new ConnectionFactory();
        factory.setHost("192.168.1.254");
        factory.setUsername("admin");
        factory.setPassword("admin123");

        var connection = factory.newConnection();
        var channel = connection.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "topic");

        var queueName = channel.queueDeclare().getQueue();

        if (argv.length < 1) {
            System.err.println("Usage: ReceiveLogsTopic [binding_key]...");
            System.exit(1);
        }

        for (var bindingKey : argv) {
            channel.queueBind(queueName, EXCHANGE_NAME, bindingKey);
        }

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

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
            System.out.println(" [x] Received '" + delivery.getEnvelope().getRoutingKey()
                    + "':'" + message + "'");
        };

        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {});
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值