我们在带有In-Memory发件箱的Saga上进行了一些负载测试。在这些测试中,我们模拟了不同类型的故障:应用程序重启,基础架构重启,消息代理重启等。
我们注意到,某些传奇实例未完成,并且出现了很多错误: Automatonymous.NotAcceptedStateMachineException:... {SomeEvent}:在状态{SomeState}中不被接受
经过一些调试,我们隔离了问题。我将尝试使用以下示例代码对其进行描述:
public class OrderStateMachine : MassTransitStateMachine<Order>
{
public OrderStateMachine()
{
InstanceState(x => x.CurrentState);
During(Initial,
When(Create).TransitionTo(New));
During(New,
When(AddItem)
.Then(x => x.Instance.Items.Add(x.Data.Name)),
When(Submit)
.ThenAsync(async x =>
{
// do something
await x.Publish(new SendEmail {Text = $"Order submitted. {x.Instance.Summary}"});
})
.TransitionTo(Submitted));
During(Submitted,
When(Accept)
.ThenAsync(async x =>
{
// do something
await x.Publish(new SendEmail {Text = $"Order accepted. {x.Instance.Summary}"});
})
.Finalize());
SetCompletedWhenFinalized();
}
public State New { get; private set; }
public State Submitted { get; private set; }
public Event<Create> Create { get; private set; }
public Event<AddItem> AddItem { get; private set; }
public Event<Submit> Submit { get; private set; }
public Event<Accept> Accept { get; private set; }
}
public class Order : SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
public IList<string> Items { get; set; } = new List<string>();
public string Summary => $"Items: {string.Join(", ", Items)}";
}
public class Create : CorrelatedBy<Guid>
{
public Guid CorrelationId { get; set; }
}
public class AddItem : CorrelatedBy<Guid>
{
public Guid CorrelationId { get; set; }
public string Name { get; set; }
}
public class Submit : CorrelatedBy<Guid>
{
public Guid CorrelationId { get; set; }
}
public class Accept : CorrelatedBy<Guid>
{
public Guid CorrelationId { get; set; }
}
public class SendEmail
{
public string Text { get; set; }
}
这是发生了什么
如果在接受事件处理过程中状态为已提交,该怎么办?我的假设:
处理这种情况的最佳解决方案是什么?我已经阅读了克里斯关于内存发件箱的出色article,但不了解当Saga处于不再处理该消息的状态时,如何在重新交付期间处理该消息。当然,我们可以使用一些棘手的逻辑在下一个状态中处理重新传递的事件,但这似乎很麻烦。我们的Saga比提供的示例要复杂得多。
也许发送了所有发件箱中的消息之后提交的事务,是否可以解决? Transaction Outbox可以以某种方式配置Saga吗?
答案 0 :(得分:1)
由于您已经阅读了有关使用发件箱的文章,并且意识到需要将unsafe
的处理程序添加到Submit
状态,所以这确实是答案。但是,与更新了传奇状态并保留的原始处理程序不同,您只需要重新生成发送/发布的事件即可。解决了问题的第一部分,已提交。
第二部分是一个不同的答案,实际上很简单。您不会在“接受”中最终确定订单。您创建一个附加状态 Accepted ,该状态在接受后即转换为。然后,您将在一段时间(一周,一个月等)之后删除订单实例。这样,当“接受”消息传递到“接受”实例时,您可以重新生成已发布的事件。
现在,您可以使用Quartz安排将来的消息来完成传奇,这不会执行任何业务逻辑,而只会删除传奇实例。您可以设置一个Initially(When(RemoveOrder).Ignore())处理程序,如果该传奇不存在,该处理程序将丢弃移除订单消息。这使其成为自动的。但是在过去的系统中,我们只是归档了文件组的日期范围分区(在SQL Server中),或者在30天或90天或任何其他天后删除了较旧的记录。