使用 RabbitMQ 和 MassTransit 在 .NET Core 中实现强大的微服务:处理订阅者故障和消息恢复

在本文中,我们将探讨如何在 .NET Core 中构建微服务,并使用 MassTransit 将 RabbitMQ 作为消息代理。我们将重点介绍如何实现发布-订阅模式,并重点强调如何确保订阅服务器能够从故障中恢复。具体来说,我们将演示订阅服务器如何在重启后重新连接、恢复正常运行,以及如何处理 RabbitMQ 停机期间排队的未处理消息。

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

第 1 节:微服务和 RabbitMQ 简介

微服务架构是一种将应用程序构建为松散耦合服务集合的方法。每个服务负责一项特定的功能,并且可以独立开发、部署和扩展。RabbitMQ 是一个消息代理,用于促进这些微服务之间的通信,支持各种消息传递模式,包括发布-订阅模式。

第 2 部分:设置环境

先决条件

确保已安装 .NET Core SDK 和 RabbitMQ。您可以使用 Docker 简化 RabbitMQ 和 .NET Core 微服务的设置。

设置新的 .NET Core 项目

dotnet new webapi -n PublisherService

dotnet new webapi -n SubscriberService

使用 MassTransit 配置 RabbitMQ

将 MassTransit 和 RabbitMQ 客户端库添加到您的项目中:

dotnet add package MassTransit

dotnet add package MassTransit.RabbitMQ

第 3 节:实现发布者服务

创建发布者服务

public class PublisherService

{

private readonly IBusControl _bus;

public PublisherService(IBusControl bus)

{

_bus = bus;

}

public async Task PublishMessage(Guid orderId)

{

await _bus.Publish(new SubmitOrder { OrderId = orderId });

Console.WriteLine(" x Sent order: {0}", orderId);

}

}

public class SubmitOrder

{

public Guid OrderId { get; set; }

}

在中配置 MassTransitProgram.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMassTransit(x =>

{

x.UsingRabbitMq((context, cfg) =>

{

cfg.Host("localhost", "/", h =>

{

h.Username("guest");

h.Password("guest");

});

});

});

builder.Services.AddMassTransitHostedService();

var app = builder.Build();

var publisher = app.Services.GetRequiredService<PublisherService>();

await publisher.PublishMessage(Guid.NewGuid());

app.Run();

第 4 节:实施订户服务

创建订阅者服务

public class SubmitOrderConsumer : IConsumer<SubmitOrder>

{

public async Task Consume(ConsumeContext<SubmitOrder> context)

{

Console.WriteLine("Processing order: " + context.Message.OrderId);

// Process the message

}

}

在中配置 MassTransitProgram.cs

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMassTransit(x =>

{

x.AddConsumer<SubmitOrderConsumer>();

x.UsingRabbitMq((context, cfg) =>

{

cfg.Host("localhost", "/", h =>

{

h.Username("guest");

h.Password("guest");

});

cfg.ReceiveEndpoint("submit_order_queue", e =>

{

e.ConfigureConsumer<SubmitOrderConsumer>(context);

});

});

});

builder.Services.AddMassTransitHostedService();

var app = builder.Build();

app.Run();

第 5 节:确保用户弹性

处理用户重启和断开连接

MassTransit 会自动处理重新连接。请确保您的消费者能够幂等地处理消息重新传递。

消息确认

MassTransit 自动处理消息确认。请确保您的消费者逻辑稳健,并能够处理重试。

配置指数重试策略

builder.Services.AddMassTransit(x =>

{

x.AddConsumer<SubmitOrderConsumer>(configurator =>

{

configurator.UseMessageRetry(r => r.Exponential(5, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(2)));

});

x.UsingRabbitMq((context, cfg) =>

{

cfg.Host("localhost", "/", h =>

{

h.Username("guest");

h.Password("guest");

});

cfg.ReceiveEndpoint("submit_order_queue", e =>

{

e.ConfigureConsumer<SubmitOrderConsumer>(context);

});

});

});

第 6 节:测试和调试

测试解决方案

模拟订阅者故障并确保系统可以恢复和处理未处理的消息。

public void SimulateFailure()

{

// Simulate failure by stopping the bus

_bus.Stop();

Console.WriteLine("Simulated failure. Reconnecting...");

_bus.Start();

}

调试常见问题

使用 RabbitMQ 的管理插件来监控队列和交换。

第 7 节:处理死信队列

设置死信队列

var factory = new ConnectionFactory() { HostName = "localhost" };

using var connection = factory.CreateConnection();

using var channel = connection.CreateModel();

channel.ExchangeDeclare(exchange: "dlx_exchange", type: ExchangeType.Direct);

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

channel.QueueBind(queue: "dead_letter_queue", exchange: "dlx_exchange", routingKey: "dlx_key");

var args = new Dictionary<string, object>

{

{ "x-dead-letter-exchange", "dlx_exchange" },

{ "x-dead-letter-routing-key", "dlx_key" }

};

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

处理死信消息

var consumer = new EventingBasicConsumer(channel);

consumer.Received += (model, ea) =>

{

var body = ea.Body.ToArray();

var message = Encoding.UTF8.GetString(body);

Console.WriteLine(" x Received from DLQ: {0}", message);

// Process the dead-lettered message

};

channel.BasicConsume(queue: "dead_letter_queue", autoAck: true, consumer: consumer);

通过设置死信队列,可以确保无法处理的消息不会丢失,并能得到适当的处理

拉动模型

优势

控制处理:

  • 优点:消费者可以控制何时提取消息,从而更好地管理处理负载。
  • 示例:消费者可以在非高峰时段提取消息以平衡负载。

简化的错误处理:

  • 优点:由于消费者控制何时提取下一条消息,因此更容易处理错误。
  • 示例:如果消费者遇到错误,它可以延迟拉取下一条消息,直到问题解决为止。

资源管理:

  • 优点:可以设计消费者仅在资源可用时才提取消息,从而防止过载。
  • 示例:消费者可以在拉取下一条消息之前检查系统资源的可用性,以确保不会使系统过载。

缺点

轮询开销:

  • 缺点:持续轮询队列中的消息可能会导致不必要的开销并增加延迟。
  • 示例:每秒轮询队列的消费者可能会导致消息处理延迟并消耗更多资源。

资源利用效率低下:

  • 缺点:轮询可能导致资源使用效率低下,因为消费者在等待消息时可能会处于空闲状态。
  • 示例:轮询队列但未发现消息的消费者仍将消耗 CPU 周期。

扩展的复杂性:

  • 缺点:与推送模型相比,在拉动模型中扩展消费者可能更为复杂。
  • 示例:管理轮询队列的多个消费者可能会导致竞争条件并增加负载平衡的复杂性。

结论

本文介绍了如何使用 .NET Core、RabbitMQ 和 MassTransit 实现强大的微服务架构,重点关注用户弹性和消息恢复。我们讨论了推送和拉取模型的优缺点,并提供了处理死信队列和配置重试策略的示例。

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

相关推荐
猫猫不是喵喵.21 分钟前
从0到1创建一个微服务项目
java·数据库·微服务·1024程序员节
htzyl2062 小时前
别急着上微前端:从 6 个场景倒推 Module Federation 2.0 与 qiankun 2.x 的选型
前端·微服务
知守观4 小时前
一个方法两个触发口:反编译 XXL-Job 后,我看清了 inner/job 子包的裸奔现状
java·spring boot·微服务
草莓熊Lotso1 天前
【C++ 基于微服务的即时通讯系统】文件存储子服务实战:UUID生成、文件IO封装与全接口单元测试
开发语言·网络·数据库·c++·微服务·架构·单元测试
1-1=03 天前
C# 数据结构和特殊类型
数据结构·c#·.net·.netcore
wdyan2976 天前
带着小白学习-微服务-5 实战:配置中心-数据库连接
数据库·学习·微服务
Pniubi6 天前
基于RabbitMQ实现延迟队列
rabbitmq
1-1=06 天前
C# IO和序列化
c#·.net·.netcore
胡写代码7 天前
把数据库连接搬进配置中心,服务起不来:spring.config.import 的 4 个必填项
spring cloud·微服务
宝桥南山7 天前
.NET 11 - 尝试一下Blazor中的新特性SupplyParameterFromTempData
microsoft·微软·c#·asp.net·.net·.netcore