我认为有3种方法可以回答这个问题
- 直接回答
- 控制客户端和服务器的情况的另一种方法
- 您不控制客户端的另一种方法
我想先给出答案 2 和 3(因为我认为它们更好),但最后我会直接给出答案……我保证!
我相信您的问题不在于您需要按服务接收消息的顺序处理消息,而是因为您有独立的客户,您不能保证服务以与发送消息的顺序相同的顺序接收它们客户。
考虑您的客户 ALPHA 和 BETA 示例。即使客户端 ALPHA 可能会在客户端 BETA 调用操作 C 之前发送请求以调用操作 2,但您无法知道服务将接收它们的顺序。这意味着即使您按照接收顺序在服务器上处理它们,这可能仍然是“错误”的顺序。
如果您同意这一点,请继续阅读。如果没有,请直接转到本文底部的直接答案;o)
方法 1:使用 WS-Transactions
如果您同时控制服务和客户端,并且客户端能够实现 WS-* SOAP 协议(例如 .Net WCF 客户端)。然后,您可以使用 WS-Transaction 协议来确保来自单个客户端的打算在单个长时间运行的事务中处理的操作肯定是以这种方式完成的。
有关如何使用 WCF 进行 WS-Transcation 的详细信息,请查看
http://msdn.microsoft.com/en-us/library/ms752261.aspx
以客户端 ALPHA 和 BETA 为例,这将使客户端 ALPHA 能够
ALPHA 在客户端启动事务的效果是将该事务流向服务器,以便它可以创建一个 DB 事务,以保证操作 1 和操作 2 一起完成或根本不完成。
如果客户端 BETA 在此过程中调用操作 3,它将被迫等待直到事务提交或中止。请注意,这可能会导致操作 3 超时,在这种情况下,BETA 需要处理该错误。
此行为适用于任何InstanceContextMode
or ConcurrencyMode
。
然而,缺点是这种长时间运行的分布式事务通常不利于可伸缩性。今天你有 10 多个客户,但未来会增长吗?客户端是否总是能够实现 WS-Transaction?如果是这样,那么这可能是一个好方法。
方法 2:使用“乐观”类型的方法
如果您需要支持许多不同的客户端类型(例如电话、浏览器等),那么您可以只允许操作以任何顺序发生,并确保服务和客户端逻辑可以处理故障。
这听起来可能很糟糕,但是
- 您可以在服务器端使用数据库事务(不是我上面提到的 WS-Transaction)来确保您的数据库始终保持一致
- 每个客户端都可以同步自己的调用,因此在您的示例中,ALPHA 会在调用 2 之前等待调用 1 完成 OK
这里的缺点是你需要仔细考虑你需要什么样的失败逻辑,并确保客户端和服务代码是健壮的并且行为适当。
在您的示例中,我提到 ALPHA 可以同步自己的调用,但是如果 BETA 在 ALPHA 调用操作 1 之后但在调用操作 2 之前调用操作 3,则可能是
- 操作 2 将失败(例如,操作 3 删除了操作 2 尝试更新的记录)在这种情况下,您需要处理该失败。
- 操作 3 将覆盖操作 3(例如,操作 3 更新一条记录,而操作 2 尝试更新同一条记录)。在这种情况下,您需要决定要做什么。您可以让操作 2 成功,在这种情况下,来自 BETA 的更新将丢失。或者您可以让操作 2 检测到记录在被 ALPHA 读取后发生了更改,然后失败,或者可能通知用户并让他们决定是否要覆盖更改。
有关 MS Entity Framework 上下文中对此的讨论,请参见
http://msdn.microsoft.com/en-us/library/bb738618.aspx
有关它的一般讨论,请参阅
http://en.wikipedia.org/wiki/Optimistic_concurrency_control
同样,在 WCF 术语中,这将适用于任何ConcurrencyMode
和InstanceContextMode
在可扩展性很重要的情况下,这种方法是最常用的方法。它的缺点是用户体验可能很差(例如,他们可能会在不知道的情况下覆盖其更改)。
直接回答原始问题
如果您肯定需要确保消息是按顺序处理的,我认为您正在寻找InstanceContextMode.Single
http://msdn.microsoft.com/en-us/library/system.servicemodel.instancecontextmode.aspx
这意味着在服务应用程序的生命周期内创建服务类的单个实例。如果您也使用ConcurrencyMode = Single
orConcurrencyMode = Reentrant
这意味着您的服务一次只会处理一条消息(因为您有一个实例并且它是单线程的)并且消息将按照接收到的顺序进行处理。
如果您使用ConcurrencyMode = multiple
,那么您的单个服务实例将在不同线程上同时处理多条消息,因此无法保证处理顺序。
这对性能的影响有多严重取决于每个服务调用执行所需的时间。如果您每秒获得 10 个并且每个花费 0.1 秒,那就没问题了。如果您每秒获得 20 个并且每个通常需要 0.1 秒,那么您将看到平均时间增加了 100%。