在.NET中掌握事件驱动架构,构建可扩展的微服务。学习C#观察者模式、RabbitMQ和Kafka等消息队列,以及通过代码示例实现解耦策略。

为什么这在2025年很重要
扩展.NET微服务不应意味着为每个新功能重写一半代码库——但通过REST调用实现的紧耦合往往导致这种情况。

想想看。你添加一个简单的"订单确认后发送折扣邮件"功能,部署后支付服务突然崩溃,因为通知服务响应太慢。这就是同步调用链的隐藏代价。

在2025年,随着.NET 9的云原生推进和微服务从金融科技到医疗保健的全面普及,事件驱动架构不再是可选项。它是解耦服务、适应变化和有序扩展的最清晰方式。

本指南涵盖:

  • • 直接调用在扩展时为何失效

  • • C#观察者模式作为概念基础

  • • 消息队列如何将其扩展到分布式系统

  • • 分步实现MassTransit

  • • 对比、权衡及不适用事件的场景

  • • 生产级系统的工具、监控和最佳实践

读完本文,你将能评估自己的架构并自信回答:"我应该调用,还是应该发布?"

.NET中紧耦合的缺陷
直接调用在扩展时为何失效
REST和gRPC非常适合同步API——但它们引入了紧密依赖:

订单 → 支付 → 通知 → 日志记录
每个服务必须知道调用谁、如何调用,以及失败时如何处理。
一个瓶颈会波及整个调用链。

当系统规模较小时,这种设计感觉很自然。但一旦扩展到10+服务,你基本上就是在用API玩叠叠乐——一步错整个塔就会倒塌。

电商示例:打破链条
设想:

  1. 1. 客户下订单

  2. 2. 订单服务保存订单

  3. 3. 调用支付服务 → 扣信用卡

  4. 4. 调用库存服务 → 预留库存

  5. 5. 调用通知服务 → 发送邮件

现在业务要求添加分析功能:实时跟踪订单频率。
结果如何?你又得修改订单服务。

每个新依赖都会使代码臃肿并拖慢部署。这就是"微服务"如何变回"分布式单体"的途径。

📊 插入图示:前后对比

左侧:REST调用链(紧耦合)
右侧:事件驱动(订单服务发布OrderPlaced;多个独立订阅者响应)
👉 事件翻转了这个模式:订单服务发布,其他所有人监听。

[点击Enter或查看完整图片]

这篇文章对你有帮助吗?
👉 如果喜欢请点赞!
👉 关注我获取更多.NET架构和性能洞察。
👉 订阅永不错过更新——开启邮件通知🔔!

2) 在C#中实现观察者模式
观察者模式是事件驱动设计的概念根源。它将主体(发布者)与观察者(订阅者)解耦。

C#事件处理程序代码

public classOrder
{ 
    public Guid Id { get; set; } 
}

publicclassOrderService
{
    // 事件用于通知订阅者
    publicevent Action<Order>? OrderPlaced;
    public void PlaceOrder(Order order)
    {
        // 保存到数据库
        Console.WriteLine($"Order {order.Id} saved.");
        OrderPlaced?.Invoke(order); // 通知所有订阅者
    }
}

// 观察者(同一进程内)
void SendEmail(Order order) => Console.WriteLine($"Email sent for {order.Id}");
void UpdateInventory(Order order) => Console.WriteLine($"Stock updated for {order.Id}");

// 使用
var orderService = new OrderService();
orderService.OrderPlaced += SendEmail;
orderService.OrderPlaced += UpdateInventory;
orderService.PlaceOrder(new Order { Id = Guid.NewGuid() });

输出:

Order 123 saved.
Email sent for 123
Stock updated for 123

🎉 仅用一个事件,多个订阅者就能响应,而订单服务无需知道它们的存在。

常见陷阱与修复

  • • 范围 → 仅限进程内工作

  • • 持久性 → 应用崩溃时事件丢失

  • • 内存泄漏 → 忘记取消订阅导致引用保留

⚠️ 注意:在长时间运行的应用中,务必取消订阅或使用弱事件模式。

UML类图

[主体-观察者关系图]

3) 扩展到分布式系统的消息队列
一旦超出单个进程,观察者模式就失效了。这时需要消息队列。

队列如何实现解耦
RabbitMQ、Azure Service Bus和Kafka等消息代理将观察者语义扩展到分布式系统:

  • • 生产者发布一次

  • • 代理处理传递、重试和持久化

  • • 消费者独立订阅

  • • 系统水平扩展

示例:订单流程

  • • 订单服务发布OrderPlaced

  • • 支付服务、库存服务和通知服务各自订阅

  • • 后来要添加分析?只需订阅。无需修改订单服务。

.NET集成的主流代理

  • • RabbitMQ → 轻量级、简单、久经考验

  • • Azure Service Bus → 云原生、企业级

  • • Kafka → 高吞吐量,非常适合事件流和事件溯源

⚠️ 注意:不要过早选择Kafka。对大多数微服务来说,RabbitMQ是最佳选择。

4) 实践实现:使用MassTransit在.NET中处理事件
理论够了——让我们实际搭建。

步骤1:定义事件契约

public record OrderPlacedEvent(Guid OrderId, DateTime OccurredAtUtc);

步骤2:发布事件

using MassTransit;
public class OrderService
{
    private readonly IPublishEndpoint _publish;
    public OrderService(IPublishEndpoint publish) => _publish = publish;

    public async Task PlaceOrderAsync(Order order, CancellationToken ct = default)
    {
        // 保存订单到数据库
        Console.WriteLine($"Order {order.Id} saved.");
        await _publish.Publish(new OrderPlacedEvent(order.Id, DateTime.UtcNow), ct);
    }
}

步骤3:在另一个服务中消费

using MassTransit;
public class SendEmailOnOrderPlaced : IConsumer<OrderPlacedEvent>
{
    public async Task Consume(ConsumeContext<OrderPlacedEvent> ctx)
    {
        var (orderId, when) = ctx.Message;
        Console.WriteLine($"[Email Service] Sending email for order {orderId}");
        await Task.CompletedTask;
    }
}

步骤4:配置MassTransit与RabbitMQ

builder.Services.AddMassTransit(x =>
{
    x.AddConsumer<SendEmailOnOrderPlaced>();
    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host("rabbitmq", "/", h =>
        {
            h.Username("guest");
            h.Password("guest");
        });
        cfg.ConfigureEndpoints(context);
    });
});

专业提示:集成OpenTelemetry来追踪跨服务事件,并在仪表板中关联发布/消费链路。

5) 方法对比:REST vs. 事件
[点击Enter或查看完整图片]

方法对比:REST vs. 事件

性能基准图表
[点击Enter或查看完整图片]

性能基准图表

6) 何时采用事件驱动架构
理想场景

  • • 需要不断演进消费者的微服务集成

  • • 订单处理、ETL和IoT等异步工作流

  • • 实时通知和仪表板

  • • 用于审计或商业智能的分析管道

应避免的情况

  • • 小型CRUD应用(开销不值得)

  • • 需要严格一致性的系统

  • • 运维能力有限的团队(代理=运维负担)

7) 必备.NET工具与监控
关键库

  • • MassTransit → 成熟的.NET服务总线库

  • • CAP → 支持RabbitMQ、Kafka、SQL Server的事件总线抽象

  • • Scrutor → 灵活的事件处理器依赖注入扫描

可观测性最佳实践

  • • Serilog → 结构化日志,将日志视为事件

  • • App.Metrics → 监控吞吐量和消费者健康状态

  • • OpenTelemetry → 事件流的端到端追踪

⚠️ 注意:始终使消费者具备幂等性。重试可能导致重复传递。

总结:关键要点

  • • .NET中的事件驱动架构解耦服务并适应变化

  • • C#观察者模式是你的概念基础

  • • .NET中的消息队列实现持久性、重试和水平扩展

  • • 混合系统(REST + 事件)在速度和弹性间达到最佳平衡

👉 下一步:本周审计代码库中的一个REST调用链。用事件+消费者对替换它。观察扩展变得多么简单。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐