C# .NetCore RabbitMQ的七种模式与多种交换机类型介绍及代码实践

RabbitMQ

RabbitMQ 在消息队列的设计中提供了七种常见的模式,分别适用于不同的业务场景。下面我将详细解释这七种模式,并为每种模式提供一个简单的 .NET 6.0 示例。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

RabbitMQ七种模式

1. 简单队列模式 (Simple Queue)

简单队列模式适用于单生产者和单消费者的场景。消息从生产者发送到队列,然后消费者接收并处理消息。

示例代码
生产者 (Producer)

using RabbitMQ.Client;
using System;
using System.Text;

public class SimpleQueueProducer
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using var connection = factory.CreateConnection();
        using var channel = connection.CreateModel();

        // 声明队列
        channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null);

        string message = "Hello RabbitMQ!";
        var body = Encoding.UTF8.GetBytes(message);

        // 发送消息
        channel.BasicPublish(exchange: "", routingKey: "hello", basicProperties: null, body: body);
        Console.WriteLine($"Sent: {message}");

        Console.ReadLine();
    }
}

消费者 (Consumer)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class SimpleQueueConsumer
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using var connection = factory.CreateConnection();
        using var channel = connection.CreateModel();

        // 声明队列
        channel.QueueDeclare(queue: "hello", durable: false, exclusive: false, autoDelete: false, arguments: null);

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine($"Received: {message}");
        };

        // 消费消息
        channel.BasicConsume(queue: "hello", autoAck: true, consumer: consumer);

        Console.ReadLine();
    }
}

2. 工作队列模式 (Work Queues)

工作队列模式允许多个消费者并行处理任务,适用于负载均衡的场景。

示例代码
生产者 (Producer)

using RabbitMQ.Client;
using System;
using System.Text;

public class WorkQueueProducer
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using var connection = factory.CreateConnection();
        using var channel = connection.CreateModel();

        // 声明队列
        channel.QueueDeclare(queue: "task_queue", durable: true, exclusive: false, autoDelete: false, arguments: null);

        for (int i = 0; i < 10; i++)
        {
            string message = $"Task {i}";
            var body = Encoding.UTF8.GetBytes(message);

            // 发送消息
            channel.BasicPublish(exchange: "", routingKey: "task_queue", basicProperties: new BasicProperties { Persistent = true }, body: body);
            Console.WriteLine($"Sent: {message}");
        }

        Console.ReadLine();
    }
}

消费者 (Consumer)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;

public class WorkQueueConsumer
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using var connection = factory.CreateConnection();
        using var channel = connection.CreateModel();

        // 声明队列
        channel.QueueDeclare(queue: "task_queue", durable: true, exclusive: false, autoDelete: false, arguments: null);
        channel.BasicQos(prefetchCount: 1, prefetchSize: 0, global: false);

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine($"Received: {message}");

            // 模拟处理时间
            Thread.Sleep(1000);

            // 手动确认消息
            channel.BasicAck(deliveryTag: ea.DeliveryTag, multiple: false);
            Console.WriteLine($"Done processing: {message}");
        };

        // 消费消息
        channel.BasicConsume(queue: "task_queue", autoAck: false, consumer: consumer);

        Console.ReadLine();
    }
}

3. 发布/订阅模式 (Publish/Subscribe)

发布/订阅模式允许生产者将消息发送到交换机,交换机将消息广播到多个队列。多个消费者可以接收并处理这些消息。

示例代码
生产者 (Producer)

using RabbitMQ.Client;
using System;
using System.Text;

public class PublishSubscribeProducer
{
    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: "logs", type: ExchangeType.Fanout);

        string message = "Broadcast message!";
        var body = Encoding.UTF8.GetBytes(message);

        // 发送消息到交换机
        channel.BasicPublish(exchange: "logs", routingKey: "", basicProperties: null, body: body);
        Console.WriteLine($"Sent: {message}");

        Console.ReadLine();
    }
}

消费者 (Consumer)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class PublishSubscribeConsumer
{
    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: "logs", type: ExchangeType.Fanout);

        // 声明队列
        var queueName = channel.QueueDeclare().QueueName;
        channel.QueueBind(queue: queueName, exchange: "logs", routingKey: "");

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine($"Received: {message}");
        };

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

        Console.ReadLine();
    }
}

4. 路由模式 (Routing)

路由模式允许生产者通过路由键发送消息,交换机根据路由键将消息发送到特定的队列。

示例代码
生产者 (Producer)

using RabbitMQ.Client;
using System;
using System.Text;

public class RoutingProducer
{
    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: "direct_logs", type: ExchangeType.Direct);

        string severity = "error"; // 路由键
        string message = "Error log message!";
        var body = Encoding.UTF8.GetBytes(message);

        // 发送消息到交换机
        channel.BasicPublish(exchange: "direct_logs", routingKey: severity, basicProperties: null, body: body);
        Console.WriteLine($"Sent: {message}");

        Console.ReadLine();
    }
}

消费者 (Consumer)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class RoutingConsumer
{
    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: "direct_logs", type: ExchangeType.Direct);

        var severity = "error"; // 订阅错误消息

        // 声明队列
        var queueName = channel.QueueDeclare().QueueName;
        channel.QueueBind(queue: queueName, exchange: "direct_logs", routingKey: severity);

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine($"Received: {message}");
        };

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

        Console.ReadLine();
    }
}

5. 主题模式 (Topics)

主题模式扩展了路由模式,允许消费者使用通配符(* 和 #)来订阅消息。

示例代码
生产者 (Producer)

using RabbitMQ.Client;
using System;
using System.Text;

public class TopicProducer
{
    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: ExchangeType.Topic);

        string routingKey = "animal.rabbit"; // 路由键
        string message = "Rabbit found!";
        var body = Encoding.UTF8.GetBytes(message);

        // 发送消息到交换机
        channel.BasicPublish(exchange: "topic_logs", routingKey: routingKey, basicProperties: null, body: body);
        Console.WriteLine($"Sent: {message}");

        Console.ReadLine();
    }
}

消费者 (Consumer)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class TopicConsumer
{
    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: ExchangeType.Topic);

        var routingKey = "animal.*"; // 使用通配符订阅所有 animal 类别的消息

        // 声明队列
        var queueName = channel.QueueDeclare().QueueName;
        channel.QueueBind(queue: queueName, exchange: "topic_logs", routingKey: routingKey);

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine($"Received: {message}");
        };

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

        Console.ReadLine();
    }
}

6. 延迟队列模式 (Dead Letter Exchange and TTL)

        在一些场景下,我们希望消息在过期后被自动删除或者转发到另一个队列。RabbitMQ 通过设置消息的生存时间 (TTL) 和死信交换机 (DLX) 来实现这一功能。

Dead Letter Exchange (DLX) 和 TTL 基本概念

        TTL (Time To Live):每条消息或队列都可以设置一个过期时间(TTL)。当消息在队列中停留超过指定时间后,消息会被自动删除,或者可以被转发到一个死信交换机(Dead Letter Exchange,DLX)中。

        Dead Letter Exchange (DLX):如果一个消息因 TTL 超时、队列满、或者被拒绝(例如 basic.reject 或 basic.nack)而无法被消费,它会被转发到一个 DLX 中。DLX 是一个备用的交换机,用来处理无法正常消费的消息。

应用场景

        定时任务:TTL 和 DLX 结合使用,可以实现消息的定时处理。例如,一个电子商务系统需要处理某些订单的过期状态。如果订单没有在一定时间内被处理,可以将过期的订单消息发送到另一个队列进行后续处理或审查。

        重试机制:当一个消息因某些原因无法立即处理时(如消费端暂时不可用),可以通过 DLX 将消息存储到一个“重试队列”,等待一段时间后再重新处理。例如,支付系统中,如果某笔支付消息处理失败,可以让它在 DLX 队列中等待重试,避免丢失消息。

        过期数据的自动清理:可以使用 TTL 来自动清理过期的数据,例如缓存系统。设置一个队列或消息的 TTL 后,消息会在过期后自动被丢弃,避免数据库或队列被过时数据占用。

        限流控制:在流量较大时,可以通过设置消息 TTL 来限制消息的生命周期,从而减少高峰期间的压力,确保队列不被过多的未处理消息填满。

示例代码
生产者 (Producer)

using RabbitMQ.Client;
using System;
using System.Text;

public class DeadLetterProducer
{
    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: "dlx_exchange", type: ExchangeType.Fanout);
        channel.QueueDeclare(queue: "dlx_queue", durable: false, exclusive: false, autoDelete: false, arguments: null);
        channel.QueueBind(queue: "dlx_queue", exchange: "dlx_exchange", routingKey: "");

        // 声明主队列并设置 TTL
        var arguments = new Dictionary<string, object>
        {
            { "x-dead-letter-exchange", "dlx_exchange" }
        };

        channel.QueueDeclare(queue: "main_queue", durable: true, exclusive: false, autoDelete: false, arguments: arguments);

        string message = "Message with TTL!";
        var body = Encoding.UTF8.GetBytes(message);

        // 发送消息到主队列
        channel.BasicPublish(exchange: "", routingKey: "main_queue", basicProperties: null, body: body);
        Console.WriteLine($"Sent: {message}");

        Console.ReadLine();
    }
}

消费者 (Consumer)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class DeadLetterConsumer
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using var connection = factory.CreateConnection();
        using var channel = connection.CreateModel();

        // 声明死信队列
        channel.QueueDeclare(queue: "dlx_queue", durable: false, exclusive: false, autoDelete: false, arguments: null);

        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine($"Received from DLX: {message}");
        };

        // 消费死信队列的消息
        channel.BasicConsume(queue: "dlx_queue", autoAck: true, consumer: consumer);

        Console.ReadLine();
    }
}

7. 请求/响应模式 (RPC)

请求/响应模式是指生产者发送请求消息,消费者处理请求并返回响应消息。典型的应用场景是服务调用。

示例代码
RPC 服务器 (Consumer/Server)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;

public class RpcServer
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using var connection = factory.CreateConnection();
        using var channel = connection.CreateModel();

        // 声明响应队列
        var replyQueue = channel.QueueDeclare().QueueName;

        // 设置消费者
        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += (model, ea) =>
        {
            var body = ea.Body.ToArray();
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine($"Received Request: {message}");

            // 处理请求并生成响应
            var response = $"Hello, {message}";

            var properties = channel.CreateBasicProperties();
            properties.CorrelationId = ea.BasicProperties.CorrelationId;

            // 发送响应消息
            channel.BasicPublish(
                exchange: "",
                routingKey: ea.BasicProperties.ReplyTo,
                basicProperties: properties,
                body: Encoding.UTF8.GetBytes(response));
        };

        // 消费请求消息
        channel.BasicConsume(queue: "rpc_queue", autoAck: true, consumer: consumer);

        Console.WriteLine("Awaiting RPC requests...");
        Console.ReadLine();
    }
}

RPC 客户端 (Producer/Client)

using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Text;
using System.Threading;

public class RpcClient
{
    public static string Call(string message)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using var connection = factory.CreateConnection();
        using var channel = connection.CreateModel();

        // 创建临时队列,用于接收响应
        var responseQueue = channel.QueueDeclare().QueueName;
        var consumer = new EventingBasicConsumer(channel);

        string response = null;

        consumer.Received += (model, ea) =>
        {
            if (ea.BasicProperties.CorrelationId == ea.BasicProperties.CorrelationId)
            {
                response = Encoding.UTF8.GetString(ea.Body.ToArray());
            }
        };

        // 发布请求消息
        var properties = channel.CreateBasicProperties();
        var correlationId = Guid.NewGuid().ToString();
        properties.CorrelationId = correlationId;
        properties.ReplyTo = responseQueue;

        channel.BasicPublish(
            exchange: "",
            routingKey: "rpc_queue",
            basicProperties: properties,
            body: Encoding.UTF8.GetBytes(message));

        // 等待响应
        while (response == null)
        {
            Thread.Sleep(100);
        }

        return response;
    }

    public static void Main(string[] args)
    {
        string message = "World";
        var response = Call(message);
        Console.WriteLine($"RPC Response: {response}");

        Console.ReadLine();
    }
}


总结

        RabbitMQ 提供了多种消息传递模式,包括简单队列、工作队列、发布/订阅、路由、主题、延迟队列和请求/响应模式等,适用于不同的场景。在 .NET 6.0 中,您可以使用 RabbitMQ.Client 库来实现这些模式。在实际应用中,您可以根据业务需求选择适合的模式进行消息传递。

        以上示例涵盖了 RabbitMQ 的常见模式,并提供了简单的生产者和消费者代码,帮助您在实际开发中快速上手 RabbitMQ。

交换机类型

        在 RabbitMQ 中,交换机(Exchange)是负责接收生产者发送的消息并根据某些规则将其路由到队列的组件。RabbitMQ 提供了几种不同类型的交换机,每种类型的交换机根据不同的路由规则将消息分发到队列。以下是常用的交换机类型:

1. Direct Exchange(直连交换机)

        描述:Direct Exchange 会将消息路由到与其绑定的队列,前提是消息的路由键(routing key)与队列的绑定键完全匹配。

用途:这种类型的交换机用于需要精确匹配路由键的场景。例如,基于特定的标签(如“info”或“error”)来发送日志信息。

示例

生产者发送消息到 direct_logs 交换机,使用 info 作为路由键。
只有绑定了 info 路由键的队列才会收到这条消息。

代码示例

// 声明 Direct Exchange
channel.ExchangeDeclare(exchange: "direct_logs", type: ExchangeType.Direct);

2. Fanout Exchange(扇形交换机)

        描述:Fanout Exchange 会将接收到的消息广播到所有绑定的队列,不考虑消息的路由键。无论生产者发送什么样的消息,所有绑定的队列都会接收到消息。

        用途:这种类型的交换机通常用于广播模式,例如,推送通知系统或日志收集系统,所有的消费者都应该接收消息。

示例

生产者发送消息到 logs 交换机。
所有绑定到 logs 交换机的队列都会收到该消息。

代码示例

// 声明 Fanout Exchange
channel.ExchangeDeclare(exchange: "logs", type: ExchangeType.Fanout);

3. Topic Exchange(主题交换机)

        描述:Topic Exchange 是一种更灵活的交换机类型,允许使用通配符(* 和 #)在路由键上进行匹配。* 可以匹配一个单词,而 # 可以匹配零个或多个单词。根据路由键的匹配规则,消息可以被路由到多个队列。

        用途:Topic Exchange 适用于复杂的路由规则,可以根据消息的多个维度(例如,“动物.兔子”,“设备.温度”等)来路由消息。

示例

生产者发送消息到 topic_logs 交换机,使用 animal.rabbit 作为路由键。
订阅了 animal.* 或 animal.rabbit 的队列都会收到该消息。

代码示例

// 声明 Topic Exchange
channel.ExchangeDeclare(exchange: "topic_logs", type: ExchangeType.Topic);

4. Headers Exchange(头交换机)

        描述:Headers Exchange 路由消息的规则基于消息的头部(message headers)而非路由键。消费者可以根据自定义的头部信息来匹配消息。与其他类型的交换机不同,Headers Exchange 使用的是消息头部中的键值对来进行路由。

        用途:这种类型的交换机适用于更复杂的消息路由需求,尤其是当需要根据多个字段来判断消息是否匹配时。

示例

生产者发送消息时,添加一个头部(如 {"format":"pdf", "priority":"high"})。
只有当队列的绑定条件与这些头部匹配时,消息才会被路由到该队列。

代码示例

// 声明 Headers Exchange
channel.ExchangeDeclare(exchange: "headers_logs", type: ExchangeType.Headers);

5. Default Exchange(默认交换机)

        描述:默认交换机是一个隐式的交换机,名称为空字符串 ""。如果生产者没有指定交换机,消息将自动发送到默认交换机。默认交换机是一个直连交换机,它会根据路由键直接将消息路由到队列。

        用途:这种交换机适用于简单的场景,生产者可以直接使用路由键将消息发送到队列,而无需显式声明交换机。

示例

生产者发送消息时,直接指定路由键 task_queue,RabbitMQ 会将消息路由到名为 task_queue 的队列。

代码示例

// 使用默认交换机
channel.BasicPublish(exchange: "", routingKey: "task_queue", basicProperties: null, body: body);

工作方式和应用场景

        RabbitMQ 提供了五种主要的交换机类型,每种交换机的工作方式和应用场景不同。通过合理选择交换机类型,可以实现更加灵活和高效的消息路由:

        Direct Exchange:精确匹配路由键,适用于一对一的消息传递。

        Fanout Exchange:广播所有消息,适用于一对多的消息传递。

        Topic Exchange:使用通配符进行路由匹配,适用于复杂的消息路由需求。

        Headers Exchange:根据消息头部的内容进行路由匹配,适用于基于多个条件进行路由的场景。

        Default Exchange:默认交换机,适用于简单的直连路由。

根据应用需求选择合适的交换机类型,有助于设计高效的消息传递系统。

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hefeng_aspnet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值