zoukankan      html  css  js  c++  java
  • RabbitMQ 原文译05--Topics

    在之前的系统中,我们改进了我们的日志系统,我们使用direct 交换机代替fanout交换机,可以实现选择性的接受日志。

     虽然使用direct 交换机改进了我们的系统,但是对于多种条件的判断,依然存在问题。如我们不仅仅想要根据日志的级别来订阅日志,同时也希望可以通过发出日志的源(即日志的生产者)来订阅。你也许已经通过unix 的工具syslog知道了这个概念,它同时通过级别(info/warn/crit...)和源(auth/cron/kern...).这种方式给了我们很大的灵活性--我们可以同时监听来自cron的critical 级别的错误消息和来自kern的所有消息。

    为了在我们的系统中实现这种功能,我们需要学习更为复杂的交换机类型 --topic

    Topic 交换机

    发送到topic的交换机不能是任意routing_key,而必须是一系列使用"."。这些单词可以是任意的单子,单通常情况是是跟当前消息有关的一些功能,例如:"stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit".你可以指定任意多个字符,单上线是255字节。

    绑定的key必须也是同样个格式,topic交换机背后的原理类似于把带有特定key的消息发送到所有跟这个key相匹配的队列上去,然而对于bindingKey这里有两个特殊的情况:

    1:"*"可以代表一个单词(是单词不是字符--即"."分割的单词)

    2:"?"可以代表0个或多个单词

    例如:

    在这个例子中,我们将要发送描述动物的消息,消息的routingkey将会有3部分组成,routingkey的第一个单词描述的是速度,第二个单词描述的是颜色,第三个单词是特殊描述: "<speed>.<colour>.<species>"。

    我们创建了三个绑定,Q1使用bingingkey"*.orange.*",Q2使用"*.*.rabbit" 和"lazy.#"

    绑定总结如下:

    Q1所有的orange动物感兴趣

    Q2想要所有的关于rabbits的消息,和所有的敢于layz动物的消息。

    一个带有"quick.orange.rabbit"routingkey的消息会两个队列都接收,"lazy.orange.elephant"的消息同业也会被转发到两个队列上。而"quick.orange.fox"的消息仅仅进入Q1,"lazy.brown.fox"的消息只能被Q2接收。"lazy.pink.rabbit"的消息将再次被转发到Q2上,即使它匹配到了队列2的两个bingdingKey。"quick.brown.fox"的消息不会匹配到任何绑定,所有消息将会被忽略。

    如果我们打破我们的约定发送带有4个单词的消息将会发生什么,例如"orange" 或者"quick.orange.male.rabbit"?答案是这些routingkey将不会匹配到任何的bingdingkey,因此将会被忽略

    但是"lazy.orange.male.rabbit",因为匹配到了"lazy.#"bingdingkey,所有可以被Q2接收,即使其有4个单词。

    Topic 交换机:Topic 交换机功能非常强大,可以完成像其他交换机那样工作。

    当队列的bingdingkey使用"#" 时,它将会接收所有的消息,不论routingkey是什么--像fanout交换机那样

    当特殊的"*"和"#"都没在bindingkey中使用时,它的行为就像direct交换机那样工作。

    汇总

    我没将要在我们的日志系统中使用"topic"交换机,我们将定我们的日志系统的routingkey有2个单词:"<facility>.<severity>"

    代码和之前的案例基本上是一样的。

     EmitLogTopic.cs:

    using System;
    using System.Linq;
    using RabbitMQ.Client;
    using System.Text;
    
    class EmitLogTopic
    {
        public static void Main(string[] args)
        {
            var factory = new ConnectionFactory() { HostName = "localhost" };
            using(var connection = factory.CreateConnection())
            using(var channel = connection.CreateModel())
            {
                channel.ExchangeDeclare(exchange: "topic_logs",
                                        type: "topic");
    
                var routingKey = (args.Length > 0) ? args[0] : "anonymous.info";
                var message = (args.Length > 1)
                              ? string.Join(" ", args.Skip( 1 ).ToArray())
                              : "Hello World!";
                var body = Encoding.UTF8.GetBytes(message);
                channel.BasicPublish(exchange: "topic_logs",
                                     routingKey: routingKey,
                                     basicProperties: null,
                                     body: body);
                Console.WriteLine(" [x] Sent '{0}':'{1}'", routingKey, message);
            }
        }
    }
    View Code

    ReceiveLogsTopic.cs:

    using System;
    using RabbitMQ.Client;
    using RabbitMQ.Client.Events;
    using System.Text;
    
    class ReceiveLogsTopic
    {
        public static void Main(string[] args)
        {
            var factory = new ConnectionFactory() { HostName = "localhost" };
            using(var connection = factory.CreateConnection())
            using(var channel = connection.CreateModel())
            {
                channel.ExchangeDeclare(exchange: "topic_logs", type: "topic");
                var queueName = channel.QueueDeclare().QueueName;
    
                if(args.Length < 1)
                {
                    Console.Error.WriteLine("Usage: {0} [binding_key...]",
                                            Environment.GetCommandLineArgs()[0]);
                    Console.WriteLine(" Press [enter] to exit.");
                    Console.ReadLine();
                    Environment.ExitCode = 1;
                    return;
                }
    
                foreach(var bindingKey in args)
                {
                    channel.QueueBind(queue: queueName,
                                      exchange: "topic_logs",
                                      routingKey: bindingKey);
                }
    
                Console.WriteLine(" [*] Waiting for messages. To exit press CTRL+C");
    
                var consumer = new EventingBasicConsumer(channel);
                consumer.Received += (model, ea) =>
                {
                    var body = ea.Body;
                    var message = Encoding.UTF8.GetString(body);
                    var routingKey = ea.RoutingKey;
                    Console.WriteLine(" [x] Received '{0}':'{1}'",
                                      routingKey,
                                      message);
                };
                channel.BasicConsume(queue: queueName,
                                     noAck: true,
                                     consumer: consumer);
    
                Console.WriteLine(" Press [enter] to exit.");
                Console.ReadLine();
            }
        }
    }
    View Code

    运行下面的案例:

    接收所有的日志:

    ReceiveLogsTopic.exe "#"
    

    接收所有的"kern"源的日志:

    ReceiveLogsTopic.exe "kern.*"
    

    或者仅仅接受"critical"的日志:

    ReceiveLogsTopic.exe "*.critical"
    

    你可以创建多个绑定:

    ReceiveLogsTopic.exe "kern.*" "*.critical"
    

    发送一个routingkey是 "kern.critical"的消息

    EmitLogTopic.exe "kern.critical" "A critical kernel error"
    
  • 相关阅读:
    NOI2018 退役记
    APIO2018 被屠记
    CTSC2018 被屠记
    SNOI2018 退役记
    O(1) long long a*b%p
    prufer编码
    杜教筛
    GCC卡常
    NOIP2017滚粗记
    UVA 10763 Foreign Exchange
  • 原文地址:https://www.cnblogs.com/grayguo/p/5581323.html
Copyright © 2011-2022 走看看