• RabbitMQ --- Publish/Subscribe(发布/订阅)


    目录

    RabbitMQ --- Hello Mr.Tua

    RabbitMQ --- Work Queues(工作队列)

    RabbitMQ --- Routing(路由)

    前言

    在第二篇文章中介绍了 Work Queues(工作队列),它适用于把一个消息发送给一个 Consumer 去处理的场景,也就是说每个 Consumer 都是处理不同的消息,如果某个 Consumer 挂了才会把未完成或没有处理的消息转发给其它的 Consumer 去处理。假如要把一个消息发送给多个 Consumer 去处理,也就是说每个 Consumer 都是处理相同的消息,这种场景就需要用到发布/订阅(Publish/Subscribe)模式,即 Producer 为发布者,Consumer 为订阅者。

    匿名交换机(Nameless exchange

    Exchange 被称为交换机,担任中转站的职责,其工作原理是 Producer 先把消息发送给 Exchange,然后再由 Exchange 指定消息按照路由规则进入队列转发给 Consumer 去处理。在前两篇文章的示例中其实已经用到了默认的 Exchange,即匿名交换机(Nameless exchange)。

    /*exchange:指定空字符串表示默认的Exchange标识 | routingKey:指定消息发送到哪个队列*/
    channel.BasicPublish(exchange, routingKey, basicProperties, body)

    交换机的类型(Exchange types

    Exchange 必须要知道如何处理它接收到的每个消息,是否让消息进入指定的队列?是否让消息进入多个队列?是否直接忽略消息?这些都取决于它的四种类型。

    1.Direct:处理路由键。需要将一个队列绑定到 Exchange 上,只允许该消息与一个特定的路由键名称完整匹配才能被转发给队列。

    /*Producer*/
    
    //定义交换机
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Direct//类型
    );
    
    channel.BasicPublish
    (
       exchange: "Tua",
       routingKey: "Mr.Tua",//路由键名称
       basicProperties: null,
       body: body
    );
    /*Consumer*/
    
    //定义交换机
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Direct
    );
    
    //绑定交换机和队列,指明如何转发消息给队列
    channel.QueueBind
    (
       queue: queueName,
       exchange: "Tua",
       routingKey: "Mr.Tua"//此处和生产者的路由键作比较,相同则转发消息,反之则不转发
    );

    2.Fanout:不处理路由键。只要将队列绑定到 Exchange 上,Producer 发送到 Exchange 的消息都会被广播到所有的队列上。 

    /*Producer*/
    
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Fanout
    );
    
    channel.BasicPublish
    (
       exchange: "Tua",
       routingKey: string.Empty,//此处若有值则会忽略
       basicProperties: null,
       body: body
    );
    /*Consumer*/
    
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Fanout
    );
    
    //绑定交换机和队列,指明如何转发消息给队列
    channel.QueueBind
    (
       queue: queueName,
       exchange: "Tua",
       routingKey: string.Empty//此处若有值则会忽略
    );

    3.Topic:将路由键和某种命名规则进行匹配,只允许匹配上的消息才能被转发给队列。符号“#”匹配一个或多个词,符号“*”匹配一个词。

    /*Producer*/
    
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Topic
    );
    
    channel.BasicPublish
    (
       exchange: "Tua",
       routingKey: "Mr.Tua.Tua"
       basicProperties: null,
       body: body
    );
    /*Consumer*/
    
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Topic
    );
    
    //绑定交换机和队列,指明如何转发消息给队列
    channel.QueueBind
    (
       queue: queueName,
       exchange: "Tua",
       routingKey: "Mr.#"//符号#可以匹配上Mr.Tua.Tua,符号*只能匹配上Mr.Tua
    );

    4.Headers:它不依赖路由键,而是在接收方绑定队列和 Exchange 时指定键值对和发送方 headers 属性中的键值对进行匹配,键值对的值可以是任何类型,只允许匹配上的消息才能被转发给队列。匹配规则 x-match 有两种类型:all 表示所有的键值对匹配;any 表示至少有一个键值对匹配。

    /*Producer*/
    
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Headers
    );
    var headers = new Dictionary<string, object>();//定义键值对集合
    headers.Add("Name", "Mr.Tua");
    headers.Add("Age", 102);
    var basicProperties = channel.CreateBasicProperties();
    basicProperties.Headers = headers;//绑定消息中的headers属性值
    
    channel.BasicPublish
    (
       exchange: "Tua",
       routingKey: string.Empty,
       basicProperties: basicProperties,
       body: body
    );
    /*Consumer*/
    
    channel.ExchangeDeclare
    (
       exchange: "Tua",
       type: ExchangeType.Headers
    );
    var headers = new Dictionary<string, object>();//定义键值对集合
    headers.Add("x-match", "any");//定义匹配规则
    headers.Add("Name", "Mr.Tua.Tua");
    headers.Add("Age", 102);
    
    channel.QueueBind
    (
       queue: queueName,
       exchange: "Tua",
       routingKey: string.Empty,
       arguments: headers//指定键值对
    );

     临时队列(Temporary queues

    当希望在生产者和消费者之间共享队列时,为队列命名是很重要的。如果希望接收所有最新的消息时,那么就可以不用关心队列名称了,需要一个新的空队列,而且当 Consumer 和 Rabbit 服务连接断开时需要自动删除该队列,这就是临时队列(Temporary queues)的作用。

    string queueName = channel.QueueDeclare().QueueName;//创建一个随机命名的唯一非持久可以自动删除的临时队列

    完整示例

    现在修改上一章的代码,通过 Fanout Exchange 使两个 Consumer 接收相同的消息:

    using RabbitMQ.Client;
    using System;
    using System.Text;
    
    namespace Producer
    {
        class Program
        {
            static void Main(string[] args)
            {
                var factory = new ConnectionFactory
                {
                    HostName = "10.202.228.107",
                    UserName = "Tua",
                    Password = "Tua",
                    Port = 5672
                };
                using (var connection = factory.CreateConnection())
                {
                    using (var channel = connection.CreateModel())
                    {
                        channel.ExchangeDeclare
                        (
                            exchange: "Tua",
                            type: ExchangeType.Fanout
                        );
                        for (int m = 0; m < 10; m++)
                        {
                            string marks = string.Empty;
                            for (int n = 0; n <= m; n++)
                            {
                                marks += ">";
                            }
                            string msg = "Mr.Tua" + marks + marks.Length + "s";
                            var body = Encoding.UTF8.GetBytes(msg);
                            channel.BasicPublish
                            (
                                exchange: "Tua",
                                routingKey: "",
                                basicProperties: null,
                                body: body
                            );
                            Console.WriteLine("Producer sent message: {0}", msg);
                        }
                        Console.ReadLine();
                    }
                }
            }
        }
    }
    Producer
    using RabbitMQ.Client;
    using RabbitMQ.Client.Events;
    using System;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace Consumer
    {
        class Program
        {
            static void Main(string[] args)
            {
                var factory = new ConnectionFactory
                {
                    HostName = "localhost"
                };
                using (var connection = factory.CreateConnection())
                {
                    using (var channel = connection.CreateModel())
                    {
                        channel.ExchangeDeclare
                        (
                            exchange: "Tua",
                            type: ExchangeType.Fanout
                        );
                        string queueName = channel.QueueDeclare().QueueName;
                        channel.QueueBind
                        (
                            queue: queueName,
                            exchange: "Tua",
                            routingKey: ""
                        );
                        var consumer = new EventingBasicConsumer(channel);
                        consumer.Received += (sender, e) =>
                        {
                            var body = e.Body;
                            var msg = Encoding.UTF8.GetString(body);
                            int marks = msg.ToCharArray().Where(c => c.ToString() == ">").Count();
                            Console.WriteLine("Consumer received message: {0}", msg);
                            Thread.Sleep(marks * 1000);
                            Console.WriteLine("OK");
                        };
                        channel.BasicConsume
                        (
                            queue: queueName,
                            noAck: true,
                            consumer: consumer
                        );
                        Console.ReadLine();
                    }
                }
            }
        }
    }
    Consumer

     

  • 相关阅读:
    根据屏幕宽度适应屏幕样式
    设计模式之命令模式
    动态代理的使用以及其实现机制
    PLSQL链接不上oracle
    struts2自定义结果类型demo
    Tomcat虚拟路径
    SEQUENCE序列
    mysql导出数据库中建表语句和数据
    Tomcat6 启动时 Cannot assign requested address: JVM_Bind
    sql常用命令
  • 原文地址:https://www.cnblogs.com/poepoe/p/7340374.html
Copyright © 2020-2023  润新知