RabbitMQ的六种工作模式

一、普通队列模式

1.  一个消费者,一个队列,一个消费者。
2.  消息产生消息放入队列,消息的消费者(consumer) 监听(while) 消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除(隐患 消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失)应用场景:聊天(中间有一个过度的服务器;p端,c端)

imageimage

  • 获取RabbitMQ连接帮助类

    后面代码,这部分创建连接共用

    public class RabbitMQHelper
        {
            /// <summary>
            /// 获取RabbitMQ连接
            /// </summary>
            /// <returns></returns>
            public static IConnection GetConnection()
            {
                //实例化连接工厂
                var factory = new ConnectionFactory
                {
                    HostName = "127.0.0.1", //ip
                    Port = 5672, // 端口
                    UserName = "Admin", // 账户
                    Password = "Admin", // 密码
                    VirtualHost = "/"   // 虚拟主机
                };
    
                return factory.CreateConnection();
            }
        }
  • 生产者

    public class Send
    {
    
        public static void SendMessage()
        {
            string queueName = "normal";
    
            //1.创建链接
            using (var connection = RabbitMQHelper.GetConnection())
            {
                // 2.创建信道
                using(var channel = connection.CreateModel())
                {
                    // 3.声明队列
                    channel.QueueDeclare(queueName, false, false, false, null);
                    // 没有绑定交换机,怎么找到路由队列的呢?
                    for (int i = 1; i <= 30; i++)
                    {
                        //4.构建Byte消息数据包
                        string message =$"第{i}条消息";
                        var body = Encoding.UTF8.GetBytes(message);//消息以二进制形式传输
    
                        // 发送消息到rabbitmq,使用rabbitmq中默认提供交换机路由,默认的路由Key和队列名称完全一致
                        //5.发送数据包
                        channel.BasicPublish(exchange: "", routingKey: queueName, null, body);
                        Thread.Sleep(1000);//添加延迟
                        Console.WriteLine("生产:" + message);
                    }
                }
            }
    
        } 
    }
  • 消费者

    public class Receive
    {
        public static void ReceiveMessage()
        {
            // 消费者消费是队列中消息
            string queueName = "normal";
            //1.建立链接链接
            var connection = RabbitMQHelper.GetConnection();
            {
                //2.建立信道
                var channel = connection.CreateModel();
                {
                    //3.声明队列:如果你先启动是消费端就会异常
                    channel.QueueDeclare(queueName, false, false, false, null);
                    //4.创建一个消费者实例
                    var consumer = new EventingBasicConsumer(channel);
                    //5.绑定消息接收后的事件委托
                    consumer.Received +=(model, ea) => {
                        var message = Encoding.UTF8.GetString(ea.Body.ToArray());
                        Thread.Sleep(1000);
                        Console.WriteLine(" Normal Received => {0}", message);
                    }; 
                    //6.启动消费者
                    channel.BasicConsume( queue: queueName, autoAck:true, consumer);//开始消费
                }
    
            }
    
        } 
    }

二、工作队列模式

  1. 一个消费者,一个队列,多个消费者。但多个消费者中只会有一个会成功地消费消息

  2. 消息产生者将消息放入队列消费者可以有多个,消费者1,消费者2,同时监听同一个队列,消息被消费?C1 C2共同争抢当前的消息队列内容,谁先拿到谁负责消费消息(隐患,高并发情况下,默认会产生某一个消息被多个消费者共同使用。

  3. 应用场景:红包;大项目中的资源调度(任务分配系统不需知道哪一个任务执行系统在空闲,直接将任务扔到消息队列中,空闲的系统自动争抢)image

  • 生产者

    public class WorkerSend
        {
    
            public static void SendMessage()
            {
                string queueName = "Worker_Queue";
    
                using (var connection = RabbitMQHelper.GetConnection())
                {
                    using(var channel = connection.CreateModel())
                    {
                        channel.QueueDeclare(queueName, false, false, false, null);
                        for (int i = 0; i < 30; i++)
                        {
                            string message = $"RabbitMQ Worker {i + 1} Message";
                            var body = Encoding.UTF8.GetBytes(message);
                            channel.BasicPublish("", queueName, null, body);
                            Console.WriteLine("send Task {0} message",i + 1);
                        }
                       
                    }
                }
                
            } 
        }
  • 消费者

    public class WorkerReceive
        {
            public static void ReceiveMessage()
            {
                string queueName = "Worker_Queue";
                var connection = RabbitMQHelper.GetConnection();
                {
                    var channel = connection.CreateModel();
                    {
                        channel.QueueDeclare(queueName, false, false, false, null);
                        var consumer = new EventingBasicConsumer(channel);
                        //设置prefetchCount : 1来告知RabbitMQ,在未收到消费端的消息确认时,不再分发消息,也就确保了当消费端处于忙碌状态时,不再分配任务。
                        channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
                        consumer.Received +=(model, ea) => {
                            var message = Encoding.UTF8.GetString(ea.Body.ToArray());
                            Console.WriteLine(" Worker Queue Received => {0}", message);
                        }; 
                        channel.BasicConsume(queueName,true, consumer);
                    }
                   
                }
              
            } 
        }

三、扇形队列模式(发布/订阅模式)

  1. 一个消息生产者,一个交换机,多个队列,多个消息消费者。每个消费队列中消息一致,且每个消息消费者都从自己的消息队列的第一个消息开始消费,直到最后。

  2. 交换机为rabbitMQ中内部组件。消息生产者将消息发送给rabbitMQ后,rabbitMQ会根据订阅的消费者个数,生成对应数目的消息队列,这样每个消费者都能获取生产者发送的全部消息。

  3. 一旦消费者断开与rabbitMQ的连接,队列就会消失。如果消费者数目很多,对于rabbitMQ而言,也是个重大负担,订阅模式是个长连接,占用并发数,且每个消费者一个队列会占用大量空间

  4. 相关应用场景:邮件群发,群聊,广播image

  • 生产者
public static void SendMessage()
        {
            //1.创建连接
            using (var connection = RabbitMQHelper.GetConnection())
            {
                //2.创建信道
                using(var channel = connection.CreateModel())
                {
                    // 3.声明交换机对象
                    channel.ExchangeDeclare("fanout_exchange", "fanout");
                   
                    // 4.创建队列
                    string queueName1 = "fanout_queue1";
                    channel.QueueDeclare(queueName1, false, false, false, null);
                    string queueName2 = "fanout_queue2";
                    channel.QueueDeclare(queueName2, false, false, false, null);
                    string queueName3 = "fanout_queue3";
                    channel.QueueDeclare(queueName3, false, false, false, null);
                    
                    // 5.绑定到交互机
                    // fanout_exchange 绑定了 3个队列 
                    channel.QueueBind(queue: queueName1, exchange: "fanout_exchange", routingKey: "");//指定交换机
                    channel.QueueBind(queue: queueName2, exchange: "fanout_exchange", routingKey: "");
                    channel.QueueBind(queue: queueName3, exchange: "fanout_exchange", routingKey: "");

                    for (int i = 0; i < 10; i++)
                    {
                        //6.构建消息byte数组
                        string message = $"RabbitMQ Fanout {i + 1} Message";
                        var body = Encoding.UTF8.GetBytes(message);
                        //7.发送消息
                        channel.BasicPublish("fanout_exchange", "", null, body);//同时把消息发送到订阅的三个队列
                        Console.WriteLine("Send Fanout {0} message",i + 1);
                    }
                }
            }
            
        } 
    }
  • 消费者
public class FanoutConsumer
    {
        public static void ConsumerMessage()
        {
            //1.创建连接
            var connection = RabbitMQHelper.GetConnection();
            {
                //2,。创建信道
                var channel = connection.CreateModel();
                {
                    //3.申明exchange
                    channel.ExchangeDeclare(exchange: "fanout_exchange", type: "fanout");
                    
                    // 4.创建队列
                    string queueName1 = "fanout_queue1";
                    channel.QueueDeclare(queueName1, false, false, false, null);
                    string queueName2 = "fanout_queue2";
                    channel.QueueDeclare(queueName2, false, false, false, null);
                    string queueName3 = "fanout_queue3";
                    channel.QueueDeclare(queueName3, false, false, false, null);
                    
                    // 5.绑定到交互机
                    channel.QueueBind(queue: queueName1, exchange: "fanout_exchange", routingKey: "");
                    channel.QueueBind(queue: queueName2, exchange: "fanout_exchange", routingKey: "");
                    channel.QueueBind(queue: queueName3, exchange: "fanout_exchange", routingKey: "");

                    Console.WriteLine("[*] Waitting for fanout logs.");

                    //6.申明consumer
                    var consumer = new EventingBasicConsumer(channel);
                    //绑定消息接收后的事件委托
                    consumer.Received += (model, ea) => {
                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body.ToArray());
                        Console.WriteLine("[x] {0}", message);

                    };
                    //7.启动消费者
                    channel.BasicConsume(queue: queueName1, autoAck: true, consumer: consumer);//只会消费队列queueName1中的消息,其他队列中订阅的消息仍然存在
                    Console.WriteLine(" Press [enter] to exit.");
                    Console.ReadLine();
                }
            }
        }
    }

四、直接队列模式(Routing路由模式)

  1. 一个消息生产者,一个交换机,多个队列,多个消息消费者。一个交换机绑定多个消息队列,每个消息队列都有自己唯一的Routekey,每一个消息队列有一个消费者监听。

  2. 消息生产者将消息发送给交换机,交换机按照路由判断,将路由到的RouteKey的消息,推送与之绑定的队列,交换机根据路由的key,只能匹配上路由key对应的消息队列,对应的消费者才能消费消息;image

  • 生产者:
public static void SendMessage()
        {
            //1.创建连接
            using (var connection = RabbitMQHelper.GetConnection())
            {
                //2.创建信道
                using(var channel = connection.CreateModel())
                {
                    // 3.声明Direct交换机
                    channel.ExchangeDeclare("direct_exchange", "direct");

                    // 4.创建队列
                    string queueName1 = "direct_queue1";
                    channel.QueueDeclare(queueName1, false, false, false, null);
                    string queueName2 = "direct_queue2";
                    channel.QueueDeclare(queueName2, false, false, false, null);
                    string queueName3 = "direct_queue3";
                    channel.QueueDeclare(queueName3, false, false, false, null);

                    // 5.绑定到交互机 指定routingKey
                    channel.QueueBind(queue: queueName1, exchange: "direct_exchange", routingKey: "red");
                    channel.QueueBind(queue: queueName2, exchange: "direct_exchange", routingKey: "yellow");
                    channel.QueueBind(queue: queueName3, exchange: "direct_exchange", routingKey: "green");

                    for (int i = 0; i < 10; i++)
                    {
                        string message = $"RabbitMQ Direct {i + 1} Message =>green";
                        var body = Encoding.UTF8.GetBytes(message);
                        // 发送消息的时候需要指定routingKey发送
                        channel.BasicPublish(exchange: "direct_exchange", routingKey: "green", null, body);//只发布到RouteKey:green的队列
                        Console.WriteLine("Send Direct {0} message",i + 1);
                    }
                }
            }
            
        } 
    }
  • 消费者
public class DirectConsumer
    {
        public static void ConsumerMessage()
        {
            //1.创建连接
            var connection = RabbitMQHelper.GetConnection();
            //2.创建通信
            var channel = connection.CreateModel();
            //3.声明交换机
            channel.ExchangeDeclare(exchange: "direct_exchange", type: "direct");
            //4.绑定交换机
            var queueName = "direct_queue2";//队列direct_queue3绑定有red,yellow,green共3个RouteKey
            channel.QueueDeclare(queueName, false, false, false, null);
            //此处消费通信没有必要绑定所有的RouteKey,根据前生产者通信的路由规则,每个队列中只会路由到一种消息
            channel.QueueBind(queue: queueName,
                                      exchange: "direct_exchange",
                                      routingKey: "red");
            channel.QueueBind(queue: queueName,
                                      exchange: "direct_exchange",
                                      routingKey: "yellow");
            channel.QueueBind(queue: queueName,
                                      exchange: "direct_exchange",
                                      routingKey: "green");

            Console.WriteLine(" [*] Waiting for messages.");

            //5.实例化消费者
            var consumer = new EventingBasicConsumer(channel);
            //6.为消费者绑定消费委托事件
            consumer.Received += (model, ea) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body.ToArray());
                var routingKey = ea.RoutingKey;
                Console.WriteLine(" [x] Received '{0}':'{1}'", routingKey, message);

                // 消费完成后需要手动签收消息,如果不写该代码就容易导致重复消费问题
                //7.手动确认签收消息
                channel.BasicAck(ea.DeliveryTag, true); // 可以降低每次签收性能损耗
            };

            // 消息签收模式
            // 手动签收 保证正确消费,不会丢消息(基于客户端而已)
            // 自动签收 容易丢消息 
            // 签收:意味着消息从队列中删除
            channel.BasicConsume(queue: queueName,
                                 autoAck: false,
                                 consumer: consumer);//设置为不自动签收,进行手动签收

            Console.WriteLine(" Press [enter] to exit.");
            Console.ReadLine();
        }
    }

五、模糊匹配队列模式(Topic 主题模式)

  1. 一个消息生产者,一个交换机,多个队列,多个消息消费者。一个交换机绑定多个消息队列,每个消息队列都有自己唯一的Routekey,每一个消息队列有一个消费者监听。

  2. 此时的自己唯一的Routekey,不是一个确定值,像我们熟悉的正则表达式对应的匹配规则。

  3. 生产者产生消息,把消息交给交换机,交换机根据RouteKey的模糊匹配到对应的队列,由队列监听消费者消费消息。

  4. 规则:

    井号和* 都是通配符,命名规则是多个单词用顿号(.)分隔开

    井号代表代表一个单词

    *代表多个单词image

  • 生产者:
public static void SendMessage()
        {
            //1.创建连接
            using (var connection = RabbitMQHelper.GetConnection())
            {
                //2.创建信道
                using (var channel = connection.CreateModel())
                {
                    //3.声明交换机
                    channel.ExchangeDeclare("topic_exchange", "topic");
                    //4.声明队列
                    string queueName1 = "topic_queue1";
                    channel.QueueDeclare(queueName1, false, false, false, null);
                    string queueName2 = "topic_queue2";
                    channel.QueueDeclare(queueName2, false, false, false, null);
                    string queueName3 = "topic_queue3";
                    channel.QueueDeclare(queueName3, false, false, false, null);
                    //5.绑定到交互机
                    channel.QueueBind(queue: queueName1, exchange: "topic_exchange", routingKey: "user.data.*");
                    channel.QueueBind(queue: queueName2, exchange: "topic_exchange", routingKey: "user.data.delete");
                    channel.QueueBind(queue: queueName3, exchange: "topic_exchange", routingKey: "user.data.update");
                
                    for (int i = 0; i < 10; i++)
                    {
                        //6.准备发送字节数组
                        string message = $"RabbitMQ Topic {i + 1} Delete Message";
                        var body = Encoding.UTF8.GetBytes(message);
                        //7.根据RouteKey发布消息
                        channel.BasicPublish("topic_exchange", "user.data.delete", null, body);//会发布到queueName1,queueName2
                        Console.WriteLine("Send Topic {0} message", i + 1);
                    }
                }
            }

        }
  • 消费者:
public static void ConsumerMessage()
        {
            //1.创建连接
            var connection = RabbitMQHelper.GetConnection();
            //2.创建通信
            var channel = connection.CreateModel();
            //3.声明交换机
            channel.ExchangeDeclare(exchange: "topic_exchange", type: "topic");
            //4.声明队列
            var queueName = "topic_queue3";
            channel.QueueDeclare(queueName, false, false, false, null);
            //5.绑定交换机
            channel.QueueBind(queue: queueName,
                                      exchange: "topic_exchange",
                                      routingKey: "user.data.*");

            Console.WriteLine(" [*] Waiting for messages.");
            //6.创建消费者
            var consumer = new EventingBasicConsumer(channel);
            //7.绑定消费委托事件
            consumer.Received += (model, ea) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body.ToArray());
                var routingKey = ea.RoutingKey;
                Console.WriteLine(" [x] Received '{0}':'{1}'", routingKey, message);
            };

            //8.启动消费
            channel.BasicConsume(queue: queueName,
                                 autoAck: true,
                                 consumer: consumer);

            Console.WriteLine(" Press [enter] to exit.");
            Console.ReadLine();
        }

六、RPC 模式(了解)

RPC即客户端远程调用服务端的方法 ,使用MQ可以实现RPC的异步调用,基于Direct交换机实现,流程如下:

1、客户端即是生产者也是消费者,向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。

2、服务端监听RPC请求队列的消息,收到消息后执行服务端的方法,得到方法返回的结果。

3、服务端将RPC方法 的结果发送到RPC响应队列。

4、客户端(RPC调用方)监听RPC响应队列,接收到RPC调用结果。

源码地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值