我试图配置以下列方式工作的Saga:
但我面临两个问题:
相关守则:
传奇的总线配置
Bus = Configure.With(Activator)
.Transport(t => t.UseRabbitMq(rabbitMqConnectionString, inputQueueName))
.Logging(l => l.ColoredConsole())
.Routing(r => r.TypeBased().MapAssemblyOf<IEventContract(publisherQueue))
.Sagas(s => {
s.StoreInSqlServer(connectionString, "Sagas", "SagaIndex");
if (enforceExclusiveAccess)
{
s.EnforceExclusiveAccess();
}
})
.Options(o =>
{
if (maxDegreeOfParallelism > 0)
{
o.SetMaxParallelism(maxDegreeOfParallelism);
}
if (maxNumberOfWorkers > 0)
{
o.SetNumberOfWorkers(maxNumberOfWorkers);
}
})
.Timeouts(t => { t.StoreInSqlServer(dcMessengerConnectionString, "Timeouts"); })
.Start();
SagaData类:
public class RouteListSagaData : ISagaData
{
public Guid Id { get; set; }
public int Revision { get; set; }
private readonly IList<LisaShippingActivity> _shippingActivities = new List<LisaShippingActivity>();
public long RoutePlanId { get; set; }
public IEnumerable<LisaShippingActivity> ShippingActivities => _shippingActivities;
public bool SentToLisa { get; set; }
public void AddShippingActivity(LisaShippingActivity shippingActivity)
{
if (!_shippingActivities.Any(x => x.Equals(shippingActivity)))
{
_shippingActivities.Add(shippingActivity);
}
}
public IEnumerable<LisaShippingActivity> GroupShippingActivitiesToLisaActivities() => LisaShippingActivity.GroupedByRouteIdAndAddress(ShippingActivities);
}
CorrelateMessages方法
protected override void CorrelateMessages(ICorrelationConfig<RouteListSagaData> config)
{
config.Correlate<ShippingOrder>(x => x.RoutePlanId, y => y.RoutePlanId);
config.Correlate<VerifyRouteListIsComplete>(x => x.RoutePlanId, y => y.RoutePlanId);
}
处理用于启动Saga的消息并发送DefferedMessage如果saga IsNew
public async Task Handle(ShippingOrder message)
{
try
{
var lisaActivity = message.AsLisaShippingActivity(_commissionerUserName);
if (Data.ShippingActivities.Contains(lisaActivity))
return;
Data.RoutePlanId = message.RoutePlanId;
Data.AddShippingActivity(lisaActivity);
var delay = TimeSpan.FromSeconds(_lisaDelayedMessageTime != 0 ? _lisaDelayedMessageTime : 60);
if (IsNew)
{
await _serviceBus.DeferLocal(delay, new VerifyRouteListIsComplete(message.RoutePlanId), _environment);
}
}
catch (Exception err)
{
Serilog.Log.Logger.Error(err, "[{SagaName}] - Error while executing Route List Saga", nameof(RouteListSaga));
throw;
}
}
最后,deffered消息的处理程序:
public Task Handle(VerifyRouteListIsComplete message)
{
try
{
if (!Data.SentToLisa)
{
var lisaData = Data.GroupShippingActivitiesToLisaActivities();
_lisaService.SyncRouteList(lisaData).Wait();
Data.SentToLisa = true;
}
MarkAsComplete();
return Task.CompletedTask;
}
catch (Exception err)
{
Serilog.Log.Error(err, "[{SagaName}] - Error sending message to LisaApp. RouteId: {RouteId}", nameof(RouteListSaga), message.RoutePlanId);
_serviceBus.DeferLocal(TimeSpan.FromSeconds(5), message, _configuration.GetSection("AppSettings")["Environment"]).Wait();
MarkAsUnchanged();
return Task.CompletedTask;
}
}
感谢任何帮助!
答案 0 :(得分:1)
我不确定我是否正确理解你正在经历的症状。
如果我“同时”向第一个处理程序发送两条消息,每个消息都会出现,甚至包含与该消息相关的属性,则在处理完第一条消息后,IsNew属性不会更改
如果调用EnforceExclusiveAccess
,我希望以串行方式处理消息,第一个消息IsNew == true
,第二个消息IsNew == false
。
如果没有,我希望这两个消息与IsNew == true
并行处理,但是 - 当插入sage数据时 - 我希望其中一个成功,另一个失败,{ {1}}。
在ConcurrencyException
之后,将再次处理该邮件,这次是ConcurrencyException
。
这不是你所经历的吗?
在第二个处理程序中,我希望访问与那些Saga相关的所有数据,但我不能,因为数据似乎是数据,因为它们在这些消息的修订中被推迟了。
你是说saga数据中的数据似乎处于延迟IsNew == false
消息时的状态?
这听起来很奇怪,你也许不太可能再试一次,看看它是不是真的如此?
更新:我发现了为什么你遇到这种奇怪的行为:你不小心设置了你的传奇处理程序实例,以便在消息中重复使用。
你是这样注册的(警告:不要这样做!):
VerifyRouteListIsComplete
然后_sagaHandler = new ShippingOrderSagaHandler(_subscriber);
_subscriber.Subscribe<ShippingOrderMessage>(_sagaHandler);
_subscriber.Subscribe<VerifyRoutePlanIsComplete>(_sagaHandler);
方法在Subscribe
上进行此调用(警告:不要这样做!):
BuiltinHandlerActivator
这就是为什么这很糟糕(特别是对于一个传奇处理程序),因为处理程序实例本身是有状态的 - 它有一个activator.Register(() => handlerInstance);
属性,包含进程的当前状态,并且还包含{{ 1}}属性。
您应该始终做的是确保每次收到消息时都会创建一个新的处理程序实例 - 您的代码应该更改为:
Data
如果将IsNew
的实现更改为:
_subscriber.Subscribe<ShippingOrderMessage>(() => new ShippingOrderSagaHandler(_subscriber)).Wait();
_subscriber.Subscribe<VerifyRoutePlanIsComplete>(() => new ShippingOrderSagaHandler(_subscriber)).Wait();
这将解决您的独家访问问题:)
您的代码还有另一个问题:您在注册处理程序和启动订阅者总线实例之间存在潜在的竞争条件,因为理论上您可能不幸并且在总线启动和注册处理程序之间开始接收消息。
您应该更改代码以确保在启动总线之前注册所有处理程序(从而开始接收消息)。