You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MassTransit SendFilter无法阻止消息发送,如何解决?

问题:自定义Outbox过滤器无法阻止MassTransit发送消息到RabbitMQ

我在多租户应用中使用MassTransit与RabbitMQ,通过IFilter实现自定义Outbox模式,只希望把消息保存到数据库,却发现消息还是会被发送到RabbitMQ。我原本以为只要不调用await next.Send(context)就能中断管道,但现在不管调不调用,消息都会被发送。以下是复现问题的简化代码,请问该如何阻止消息发送?

复现代码

using MassTransit;
namespace Test;
public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);
        IServiceCollection services = builder.Services;

        services.AddMassTransit(x =>
        {
            x.UsingRabbitMq((context, cfg) =>
            {
                // 拦截发送操作
                cfg.UseSendFilter(typeof(OutboxPreSendFilter<>), context);

                cfg.Host("localhost", 5672, "/", h =>
                {
                    h.Username("test");
                    h.Password("test");
                });
            });
        });

        var app = builder.Build();

        using var scope = app.Services.CreateScope();
        var sendEndpointProvider = scope.ServiceProvider.GetRequiredService<ISendEndpointProvider>();
        // 尝试发送消息到RabbitMQ
        await SendAndReceiveFromQueue(sendEndpointProvider);

        app.Run();
    }

    public static async Task SendAndReceiveFromQueue(ISendEndpointProvider sendEndpointProvider)
    {
        var messageToSent = Guid.NewGuid().ToString();
        var sendEndpoint = await sendEndpointProvider.GetSendEndpoint(new Uri($"queue:{nameof(TestMsg)}"));
        // 发送消息
        await sendEndpoint.Send(new TestMsg() { Id = 1, Name = messageToSent, Description = $"{DateTime.Now:f}" });
    }
}

public class OutboxPreSendFilter<T> : IFilter<SendContext<T>> where T : class
{
    public async Task Send(SendContext<T> context, IPipe<SendContext<T>> next)
    {
        // 这里执行保存消息到数据库的逻辑
 
        // 不想发送消息,所以注释了下面这行
        // await next.Send(context);
    }

    public void Probe(ProbeContext context)
    {
        context.CreateFilterScope("outboxPreSendFilter");
    }
}

public class TestMsg
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public string? Description { get; set; }
}

解决方案

1. 修正过滤器注册方式

把RabbitMQ传输级的过滤器改为全局发送过滤器,确保所有Send操作都被拦截:

services.AddMassTransit(x =>
{
    // 添加全局发送过滤器,覆盖所有发送场景
    x.AddSendFilter(typeof(OutboxPreSendFilter<>), context);

    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host("localhost", 5672, "/", h =>
        {
            h.Username("test");
            h.Password("test");
        });
    });
});

2. 在过滤器中显式取消发送上下文

仅不调用next.Send(context)可能不足以阻止发送(部分MassTransit版本存在逻辑差异),需要显式取消上下文,确保后续发送逻辑终止:

public class OutboxPreSendFilter<T> : IFilter<SendContext<T>> where T : class
{
    public async Task Send(SendContext<T> context, IPipe<SendContext<T>> next)
    {
        // 执行保存消息到数据库的逻辑
        // 例如:await _dbContext.OutboxMessages.AddAsync(...);
        // await _dbContext.SaveChangesAsync();

        // 显式取消发送上下文,阻止后续发送操作
        context.Cancel();

        // 不要调用next.Send(context)
    }

    public void Probe(ProbeContext context)
    {
        context.CreateFilterScope("outboxPreSendFilter");
    }
}

原因说明

  • 原注册方式cfg.UseSendFilter仅针对RabbitMQ传输层的发送操作,无法覆盖SendEndpoint.Send这类全局发送场景,导致过滤器未生效。
  • 部分MassTransit版本中,即使不调用next.Send(context),底层默认逻辑仍可能执行发送,显式调用context.Cancel()会让后续发送逻辑检查到上下文已取消,从而终止发送流程。

内容的提问来源于stack exchange,提问作者Antonio Padrin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:17:05