Saga处理无法投递的邮件

时间:2018-03-10 01:21:33

标签: masstransit

来自文档here

  

如果端点的配置发生变化,或者是否有消息   错误地发送到端点,可能是消息类型   收到的没有任何连接的消费者。如果发生这种情况   邮件被移动到_skipped队列(前缀为原始队列)   队列名称)。原始邮件内容将保留,另外还有   添加标头以指示移动消息的主机。

当一些消息试图传递给saga但是saga实例还没有创建时,我是否应该期待一个saga的相同行为? 换句话说,如果想要在传奇中处理消息,但该消息不是传奇创建的触发器。我希望在_skipped队列中看到这些消息,但实际上我没有在_skipped或_error队列中看到它们。此外,我可以看到按摩成功传递到传奇队列,并成功地以某种方式消耗没有任何警告或错误。谁消费了这条消息?

已更新

状态机:

public class TestState : SagaStateMachineInstance, IVersionedSaga
{
    public Guid CorrelationId { get; set; }
    public Guid Id { get; set; }
    public string CurrentState { get; set; }
    public int Version { get; set; }
    public DateTime TimeStart { get; set; }
    public int Progress { get; set; }
}

public class TestStateMachine : MassTransitStateMachine<TestState>
{
    public TestStateMachine()
    {
        InstanceState(x => x.CurrentState);

        Event(() => ProcessingStarted, x => x.CorrelateById(context => context.Message.Id));
        Event(() => ProgressUpdated, x => x.CorrelateById(context => context.Message.Id));
        Event(() => ProcessingFinished, x => x.CorrelateById(context => context.Message.Id));

        Initially(
            When(ProcessingStarted)
                .Then(context =>
                {
                    context.Instance.TimeStart = DateTime.Now;
                })
                .TransitionTo(Processing)
        );

        During(Processing,
            When(ProgressUpdated)
                .Then(context =>
                {
                    context.Instance.Progress = context.Data.Progress;
                }),
            When(ProcessingFinished)
                .TransitionTo(Processed)
                .ThenAsync(async context =>
                {
                    await context.Raise(AllDone);
                })
        );

        During(Processed,
            When(AllDone)
                .Then(context =>
                {
                    Log.Information($"Saga {context.Instance.Id} finished");
                })
                .Finalize());

        SetCompletedWhenFinalized();
    }

    public State Processing { get; set; }
    public State Processed { get; set; }

    public Event AllDone { get; set; }

    public Event<ProcessingStarted> ProcessingStarted { get; set; }
    public Event<ProgressUpdated> ProgressUpdated { get; set; }
    public Event<ProcessingFinished> ProcessingFinished { get; set; }
}

public interface ProcessingStarted
{
    Guid Id { get; }
}

public interface ProgressUpdated
{
    Guid Id { get; }
    int Progress { get; }
}

public interface ProcessingFinished
{
    Guid Id { get; }
}

配置

var repository = new MongoDbSagaRepository<TestState>(Environment.ExpandEnvironmentVariables(configuration["MongoDb:ConnectionString"]), "sagas");

var services = new ServiceCollection();

services.AddSingleton(context => Bus.Factory.CreateUsingRabbitMq(x =>
{
    IRabbitMqHost host = x.Host(new Uri(Environment.ExpandEnvironmentVariables(configuration["MassTransit:ConnectionString"])), h => { });

    x.ReceiveEndpoint(host, "receiver_saga_queue", e =>
    {
        e.StateMachineSaga(new TestStateMachine(), repository);
    });

    x.UseRetry(r =>
    {
        r.Incremental(10, TimeSpan.FromMilliseconds(10), TimeSpan.FromMilliseconds(50));
        r.Handle<MongoDbConcurrencyException>();
        r.Handle<UnhandledEventException>();
    });

    x.UseSerilog();
}));

var container = services.BuildServiceProvider();

var busControl = container.GetRequiredService<IBusControl>();

busControl.Start();

稍后,当我发布ProgressUpdated事件

busControl.Publish<ProgressUpdated>(new
{
    Id = id,
    Progress = 50
});

我希望它会引发UnhandledEventException并将按摩移动到_error队列但实际上我可以看到该消息出现在队列中,以某种方式消耗,我在_error或_skipped队列中都看不到它。 MongoDB Saga存储也没有创建任何新的saga实例。

我做错了什么?我需要配置saga的方式是,如果在未创建saga实例时获得ProgressUpdated事件,则稍后在实例准备好接受它时将重试它。

将事件ProgressUpdatedProcessingFinished重新配置为

Event(() => ProgressUpdated, x =>
{
    x.CorrelateById(context => context.Message.Id);

    x.OnMissingInstance(m => m.Fault());
});
Event(() => ProcessingFinished, x =>
{
    x.CorrelateById(context => context.Message.Id);

    x.OnMissingInstance(m => m.Fault());
});

并稍后在SagaException中抓取UseRetry

r.Handle<SagaException>();

完成了工作!

感谢@Alexey Zimarev分享知识!

1 个答案:

答案 0 :(得分:1)

如果消息应由传奇处理,但没有匹配的实例,则会调用OnMissingInstance回调。

重新配置ProgressUpdatedProcessingFinished

Event(() => ProgressUpdated, x =>
{
    x.CorrelateById(context => context.Message.Id);
    x.OnMissingInstance(m => m.Fault());
});
Event(() => ProcessingFinished, x =>
{
    x.CorrelateById(context => context.Message.Id);
    x.OnMissingInstance(m => m.Fault());
});

并在重试过滤器中捕获SagaException

r.Handle<SagaException>();