我有一个可以由用户启动的任务,可能需要数小时才能运行,以及用户在运行期间多次启动任务的合理机会。 我已将任务的处理分解为较小的批次,但数据看起来很难分辨出仍有待处理的内容。我使用每个处理一小块数据的消息对其进行批处理。
我曾想过使用Saga控制对启动此过程的访问,使用名为class MyViewModel {
}
class MyCustomView extends View {
}
class MyAnotherCustomView extends MyCustomView {
}
的Saga属性,我在处理程序的开头设置,然后在处理程序的末尾取消设置。处理程序执行一些工作并发送消息来处理数据。我检查处理程序开头的值,如果已设置,则返回。
我将Azure存储用于Saga存储,如果它对下一位产生影响。我也在使用NSB 6
我有几个问题:
答案 0 :(得分:0)
经过大量测试后
我不相信这是正确的做法。 正如Archer所说,你可以随意操纵saga数据属性,它们只保存在处理程序的末尾。
因此,如果saga收到两个同时发送的消息,Processing
的检查将同时传递,并且我将运行两个进程(在我的情况下,处理相同的数据两次)。
传奇中的传奇也面临着类似的问题。
我认为可以使用(并且在我的PoC测试期间完成)是使用数据库唯一索引来帮助。我正在使用实体框架和azure sql,因此数据库访问不包含在处理程序的事务中(这是数据库和saga数据之间的重要区别)。该数据库还可以在端点的所有实例上运行,通常看起来是一个很好的解决方案。
我正在使用的表中包含构成saga'id'的每个列,并且它们上面有唯一的索引。
在处理程序的开头,我从数据库中检索一行。如果是一行,则处理程序返回(在我的情况下,这是可以的,在其他情况下,您可以抛出异常以使处理程序再次运行)。处理程序所做的第一件事(在任何工作之前,虽然我不是100%确定它很重要)是在表中写一行。如果写入失败(可能是因为违反了唯一约束),则异常会将消息放回队列中。数据库写入失败的原因并不重要,因为NSB会处理它。
然后处理程序完成工作。
然后删除该行。 当然,在处理工作期间有可能发生某些事情,所以我也使用时间戳和另一个进程来重置它,如果它忙了太久。 (仍然需要定义'太长'但是:))
也许这可以帮助有类似问题的人。
答案 1 :(得分:0)
似乎在特定软件谷歌群组中交叉发布:
Sagas经常被用于这种模式。传奇实例将跟踪进度并防止(子)任务未被多次调用,但如果预期任务未完成或正在/随时间推移,也可以采取措施。
传真实例数据在处理消息后存储,而不是在更新任何传奇数据属性时存储。你描述的逻辑不起作用。
正确的方法是制作一个能够协调你的过程并拥有执行实际工作的常规处理程序的传奇。
在saga句柄方法中创建传奇检查,如果已经创建了传奇或已经忙碌了#39;状态,如果没有此状态,则发送消息以执行某些操作。这将保护任务仅在一次之后启动,之后存储传奇。 处理程序现在可以执行实际任务,完成后它可以执行“回复”操作。回到传奇 当传奇收到回复后,它现在可以启动任何其他后续任务或举办活动,它也可以“#”完成。
如果收到两条创建/更新同一个saga实例的消息,则只有第一个作者获胜。另一个因为乐观的并发控制而失败。
但是,如果这些消息不是并行处理的,但顺序都会失败,除非saga检查saga实例是否已经初始化。
客户端发送两个相同的邮件正文。由于乐观的并发控制,saga已启动并且只有1条消息成功。
由于重试最终会处理第二个副本,但是saga会检查一个字段的saga数据,它通常会通过“启动”消息初始化该字段。传奇。如果该字段已经初始化,则假定消息已经处理并且只返回:
它还演示了批次发送。在所有处理程序/传奇完成之前,不会立即发送消息。
以下视频可能会帮助您设计传单并了解各种模式:
与NServiceBus的集成模式:https://www.youtube.com/watch?v=BK8JPp8prXc
请记住,Azure存储不是事务性的,并且不提供锁定,它只是原子的。您在处理程序或saga中执行的任何工作都可能被多次调用,如果您使用非事务性资源,那么请确保逻辑是幂等的。