NServiceBus:超时由多个Sagas

时间:2018-06-04 07:29:55

标签: c# nservicebus nservicebus5

我们目前有一个NServiceBus 5系统,其中包含两个重复的Sagas。由于它们充当调度程序以定期从外部系统中提取多种数据,因此我们使用超时来触发:我们创建了一个名为ExecuteTask的通用空类,Saga用它来处理超时。

public class ScheduleSaga1 : Saga<SchedulerSagaData>,
    IAmStartedByMessages<StartScheduleSaga1>,
    IHandleMessages<StopSchedulingSaga>,
    IHandleTimeouts<ExecuteTask>

另一个Saga几乎同样定义:

public class ScheduleSaga2: Saga<SchedulerSagaData>,
    IAmStartedByMessages<StartScheduleSaga2>,
    IHandleMessages<StopSchedulingSaga>,
    IHandleTimeouts<ExecuteTask>

两个Sagas中的超时处理均等:

    public void Handle(StartScheduleSaga1 message)
    {
        if (_schedulingService.IsDisabled())
        {
            _logger.Info($"Task '{message.TaskName}' is disabled!");
        }
        else
        {
            Debugger.DoDebug($"Scheduling '{message.TaskName}' started!");
            Data.TaskName = message.TaskName;

            // Check to avoid that if the saga is already started, don't initiate any more tasks
            // as those timeout messages will arrive when the specified time is up.
            if (!Data.IsTaskAlreadyScheduled)
            {
                // Setup a timeout for the specified interval for the task to be executed.
                Data.IsTaskAlreadyScheduled = true;

                // Send the first Message Immediately!
                SendMessage();

                // Set the timeout
                var timeout = _schedulingService.GetTimeout();
                RequestTimeout<ExecuteTask>(timeout);
            }
        }
    }

    public void Timeout(ExecuteTask state)
    {
        if (_schedulingService.IsDisabled())
        {
            _logger.Info($"Task '{Data.TaskName}' is disabled!");
        }
        else
        {
            SendMessage();

            // Action that gets executed when the specified time is up
            var timeout = _schedulingService.GetTimeout();
            Debugger.DoDebug($"Request timeout for Task '{Data.TaskName}' set to {timeout}!");
            RequestTimeout<ExecuteTask>(timeout);
        }
    }


    private void SendMessage()
    {
        // Send the Message to the bus so that the handler can handle it
        Bus.Send(EndpointConfig.EndpointName, Activator.CreateInstance(typeof(PullData1Request)));
    }

现在的问题是:由于两个Sagas都在为ExecuteTask请求超时,因此它会被发送给两个Sagas! 更糟糕的是,似乎Sagas中的有状态数据搞砸了,因为两个Sagas都发送了这两个消息。

因此,似乎超时会被发送到请求它的所有Saga实例。 但是看一下示例https://docs.particular.net/samples/saga/simple/,没有关于多个Saga实例及其状态的特殊逻辑。

我的假设是否正确?如果是这种情况,有多个Sagas请求和接收超时的最佳做法是什么?

1 个答案:

答案 0 :(得分:1)

我能想到的唯一原因是它们共享相同的标识符以唯一地标识传奇实例。

ScheduleSaga1ScheduleSaga2都使用相同的SchedulerSagaData来存储状态。 NServiceBus根据传入消息中的唯一标识符查看传入消息并尝试检索状态。例如,如果StartScheduleSaga1StartScheduleSaga2同时带有标识符1,则NServiceBus将在表SchedulerSagaData中搜索具有唯一标识符1的saga状态。

然后ScheduleSaga1ScheduleSaga2将共享同一行!!!

超时基于SagaId表格中的TimeoutEntity。因为两个传奇共享相同的SagaId,所以它们合乎逻辑,一旦超时到达,它们都会被执行。

您至少不应重复使用标识符来安排任务。最好不要共享同一个类来存储saga状态。也更容易调试。