从MediatR到System.Threading.Channels:.NET原生消息总线架构的重构实践与性能优势

作者:微信公众号:【架构师老卢】
5-1 8:43
7

多年来,MediatR 一直是.NET 领域的明星工具。它通过中介者模式为复杂应用带来了清晰的解耦架构。需要跨层发送命令?触发通知?接入日志记录或验证等行为?MediatR 轻松实现这一切。

但最近,风向变了。

未来可能采用商业许可的传闻让我们团队陷入思考:如果核心应用逻辑被锁定在一个前景不明朗的库中,我们能否承受这种风险?

更深层的问题随之浮现: 我们真的还需要 MediatR 吗?

事实证明,.NET 的基类库(BCL)早已内置了我们所需的一切——甚至更多。


为何重新评估 MediatR? 🤔

  1. 许可不确定性
    没有人希望基础库产生意外成本。从 MIT 协议转向商业许可的动向,不仅引发成本担忧,更关乎架构的长期控制权。

  2. 过度抽象的代价
    MediatR 的优雅需付出代价:
    • 管道(Pipelines)

    • 装饰器(Decorators)

    • 通知(Notifications)

    即使简单的发布/订阅场景,对小团队和轻量级应用而言,抽象层似乎已超越实际收益。

  3. 控制力受限
    需要自定义重试逻辑?死信队列?消息优先级?
    要么硬编码扩展行为,要么费力调整库以适配需求。


✨ 原生替代方案:System.Threading.Channels
Channels —— .NET BCL 中的低调英雄。

最初为高吞吐量生产者/消费者场景设计,Channels 提供零依赖的高性能内存消息队列:无需魔法反射,完全掌控底层。


🛠️ 我们如何构建自有消息总线

  1. 原生内存队列
internal sealed class InMemoryMessageQueue
{
    private readonly Channel<IIntegrationEvent> _channel =
        Channel.CreateUnbounded<IIntegrationEvent>();
    public ChannelReader<IIntegrationEvent> Reader => _channel.Reader;
    public ChannelWriter<IIntegrationEvent> Writer => _channel.Writer;
}

开箱即用的线程安全队列,原生支持背压(Backpressure)与取消令牌。

  1. 自定义事件总线
internal sealed class EventBus : IEventBus
{
    private readonly InMemoryMessageQueue _queue;
    public EventBus(InMemoryMessageQueue queue) => 
        _queue = queue;

    public async Task PublishAsync<T>(
        T integrationEvent, 
        CancellationToken cancellationToken = default)
        where T : class, IIntegrationEvent
    {
        await _queue.Writer.WriteAsync(integrationEvent, cancellationToken);
    }
}

无装饰器、无 ServiceCollection 配置。仅关注写入与读取。

消费者端可运行简单后台服务,从通道读取并分发事件。


🎉 核心优势
✅ 零外部依赖
减少 NuGet 包 = 降低生产环境风险。代码自主可控,无需等待第三方维护更新。

✅ 完全透明
需添加日志?批处理?限流?
管道由你定义。无隐式行为注入,代码清晰可读。

✅ 经过验证的性能
Channels 驱动 ASP.NET Core 内核,专为高负载场景设计。

✅ 面向未来
即使 MediatR 或其他依赖转向,核心架构稳如磐石。你掌握底层控制权。


🚗 但这不是重复造轮子吗?
若依赖 MediatR 的完整功能集,或许如此。

但对简单命令派发或一次性通知,Channels 已足够:
• 通过有界通道实现背压

• 使用异步中间件实现重试与死信队列

• 代码量不足 50 行

差异在于:你并非重复发明轮子,而是跳过冗余步骤,直接构建更高效、更契合需求的解决方案。

相关留言评论
昵称:
邮箱:
阅读排行