重用 NServiceBus 消息传奇数据

发布于 2024-11-14 00:49:43 字数 873 浏览 1 评论 0原文

是否有任何理由不在 saga 数据中重用 NServiceBus 消息 (IMessage)?我的意思是有一条像这样的消息:

public class Order : IMessage
{
   public virtual List<TillOrderLine> OrderLines { get; set; }
}

public class TillOrderLine : IMessage { ... }

然后也在 saga 和 saga 数据中使用它,如下所示:

public class OrderProcessingSaga : 
    Saga<OrderProcessingSagaData>, 
    IAmStartedByMessages<Order> { ... }

public class OrderProcessingSagaData : ISagaEntity
{
   public virtual Guid Id { get; set; }
   public virtual string Originator { get; set; }
   public virtual string OriginalMessageId { get; set; }

   // The message data is stored by the saga here.
   public virtual Order Order { get; set; }
}

我意识到消息是由传输层(MSMQ)存储的,而 saga 数据则通过以下方式保存到数据库:传奇。它适用于我当前的用例,并且重用该类似乎更优雅,而不是为消息创建一个类,为传奇存储创建另一个类。

我想知道这种方法是否有任何问题?

Are there any reasons not to reuse an NServiceBus message (IMessage) in saga data? What I mean is having a message something like this:

public class Order : IMessage
{
   public virtual List<TillOrderLine> OrderLines { get; set; }
}

public class TillOrderLine : IMessage { ... }

And then also using it in the saga and saga data like this:

public class OrderProcessingSaga : 
    Saga<OrderProcessingSagaData>, 
    IAmStartedByMessages<Order> { ... }

public class OrderProcessingSagaData : ISagaEntity
{
   public virtual Guid Id { get; set; }
   public virtual string Originator { get; set; }
   public virtual string OriginalMessageId { get; set; }

   // The message data is stored by the saga here.
   public virtual Order Order { get; set; }
}

I realise that the message is stored by the transport layer (MSMQ), whilst the saga data is persisted to DB with the saga. It works for my current use case and seems more elegant to reuse the class rather than creating one for the message and another for saga storage.

I'm wondering if there are any gotchas with this approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一江春梦 2024-11-21 00:49:43

我同意 mookid8000 所说的一切,但我还有一个细节需要补充。

您的传奇存储通常会存在很多争用。为了保证一致性,saga 存储提供程序通常会在数据上放置更新锁,以保证来自同一 saga 的另一条消息不能同时改变状态。

如果您使用默认的 NHibernate saga 持久化程序(并且您的虚拟属性向我表明您是这样),那么 NHibernate 使用一些假设来保存您的数据:

  • saga 数据中的复杂类型将导致数据库列遵循 ComplexTypePropName_ChildPropertyName 语法(或大约 - 此处根据内存操作)。因此,您的传奇数据仍将包含在一张表的 1 行内。
  • 如果您的 saga 数据中有一个列表,那么它显然必须创建一个新表来存储列表项。现在,您的传奇数据包含在一个主行和多个详细行中。
    • 为了让 NHibernate 映射它,您的集合类型还必须有一个名为 Id 的 Guid 属性,这意味着您无法存储基元列表,并且您的 TillOrderLine 类(公共消息的一部分)将需要此 Id 属性这对您的服务的商业目的毫无意义。
  • 我不知道如果你的传奇数据中有一个复杂的类型(订单,基本上是重用的消息),它本身包含一个(TillOrderLines)列表,那么你的情况会发生什么。

无论如何,saga persister 需要将数据拆分成的表越多,就越难使用细粒度行锁来锁定涉及的行。如果有足够的争用,行锁将开始升级为页锁和表锁,然后你就真的遇到了问题。

这就是为什么文档数据库真正适合 saga 存储,这就是为什么在 NServiceBus 3.0 中他们将 RavenDB 设为默认的 saga 持久化器。

在此之前,请查看基于 XML 序列化的 Saga Persister我写道,它可以为您今天的应用程序带来即将推出的 RavenDB 风格的传奇持久化程序的一些好处。

如果您必须坚持使用 NHibernate,我强烈建议您不要将消息与 saga 持久性解决方案紧密结合在一起。我几乎可以保证它会在路上再次困扰你。

I agree with everything mookid8000 said, however I have one more detail to add.

There can commonly be a lot of contention on your saga storage. To guarantee consistency, the saga storage provider will commonly place update locks on the data to guarantee that another message from the same saga can't mutate state at the same time.

If you are using the default NHibernate saga persister (and your virtual properties suggests to me that you are) then NHibernate uses some assumptions in order to save your data:

  • A complex type in your saga data will result in database columns following a ComplexTypePropName_ChildPropertyName syntax (or thereabouts - operating from memory here). So your saga data will still be contained within 1 row in one table.
  • If you have a list in your saga data, it must obviously create a new table to store the list items. Now your saga data is contained in one master row and multiple detail rows.
    • In order for NHibernate to map this, your collection type must also have a Guid propery named Id, meaning you can't store a list of primitives, and your TillOrderLine class (part of the public message) would need this Id property which makes no sense to the business purpose of your service.
  • I don't know what happens in your case if you have a complex type in your saga data (Order, basically the message reused) which itself contains a list (of TillOrderLines).

In any case, the more rows in the more tables the saga persister is required to split your data into, the harder it is to lock the involved rows with granular row locks. With enough contention, row locks will start to escalate to page locks and table locks and then you've really got a problem.

This is why a document database would really shine for saga storage, which is why in NServiceBus 3.0 they are making RavenDB the default saga persister.

Until then, take a look at the XML Serialization based Saga Persister I wrote, which can bring some of the benefits of the upcoming RavenDB-style saga persister to your application today.

If you must stick with NHibernate, I would strongly discourage you to couple your messages so tightly with your saga persistence solution. I almost guarantee it will come back to haunt you down the road.

无人问我粥可暖 2024-11-21 00:49:43

如果您当前的传奇持久化者没有抱怨地坚持数据,我想不出这种方法有什么问题 - 只要您意识到您正在出于两个目的重用一个类,从而在传奇数据(这是私有的)之间引入耦合 。

然后,如果您需要偏离消息的结构方式,您可以为所有 saga 数据构建类

PS:如果 TillOrderLine 的唯一目的是在 Order 内聚合,则无需将其标记为 IMessage

If your surrent saga persister persists the data without complaining, I cannot think of anything wrong with this approach - as long as you're conscious that you're reusing a class for two purposes, thus introducing a coupling between saga data (which is private to the service) and messages (which are inherently public) regarding fields present, naming, etc.

Then you can build classes for all of your saga data if and when you need to deviate from the ways your messages are structured.

PS: TillOrderLine doesn't need to be marked as IMessage if its only purpose is to be aggregated inside Order.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文