1

我有一个将为多个客户端提供服务的 WCF 服务。它们将具有“接受建议匹配”或“拒绝建议匹配”等操作。我希望所有操作都串行运行,但我担心这会对性能产生影响。

据我所见 - 默认(也是最常用的)实例是“每次调用”,默认并发模式是单一的。

那么我是否需要在并发中使用“单”模式?它的影响有多严重?

(我估计最多有几十个客户使用该系统)。

有什么方法可以享受两个世界吗?

使用并行计算(服务与数据库通信)并且仍然串行执行操作?

在这篇文章中,我读到了“实例模式 = 每次调用和并发 = 单一”:

  • 对于每个客户端实例,将分配一个线程。
  • 对于每个方法调用,都会创建一个新的服务实例。
  • 单个线程将用于为从单个客户端实例生成的所有 WCF 实例提供服务。

对我来说,这似乎并不能保证操作调用将连续执行!

例如,如果发生这种情况:

  1. 客户 ALPHA 调用“操作 A”
  2. 客户 ALPHA 调用“操作 B”
  3. 客户端测试版调用“操作 C”

从我读到的 - 它说'单个线程将用于服务从单个客户端实例生成的所有 WCF 实例'。在我看来,这意味着 CALL 1 和 CALL 2 将连续执行,但 CALL 3 可能在它们之间执行,因为它来自不同的客户端线程。

这是真的 ?有没有办法确保呼叫按到达的顺序处理?

有人可以告诉我这是否是一种不好的做法,或者是否存在接受并建议在 WCF 中使用这种通信方法的现实世界场景。

非常感谢

4

1 回答 1

2

我认为有3种方法可以回答这个问题

  1. 直接回答
  2. 控制客户端和服务器的情况的另一种方法
  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 能够

  • 开始交易
  • 调用操作 1
  • 调用操作 2
  • 提交交易

ALPHA 在客户端启动事务的效果是将该事务流向服务器,以便它可以创建一个 DB 事务,以保证操作 1 和操作 2 一起完成或根本不完成。

如果客户端 BETA 在此过程中调用操作 3,它将被迫等待直到事务提交或中止。请注意,这可能会导致操作 3 超时,在这种情况下,BETA 需要处理该错误。

此行为适用于任何InstanceContextModeor ConcurrencyMode

然而,缺点是这种长时间运行的分布式事务通常不利于可伸缩性。今天你有 10 多个客户,但未来会增长吗?客户端是否总是能够实现 WS-Transaction?如果是这样,那么这可能是一个好方法。

方法 2:使用“乐观”类型的方法

如果您需要支持许多不同的客户端类型(例如电话、浏览器等),那么您可以只允许操作以任何顺序发生,并确保服务和客户端逻辑可以处理故障。

这听起来可能很糟糕,但是

  • 您可以在服务器端使用数据库事务(不是我上面提到的 WS-Transaction)来确保您的数据库始终保持一致
  • 每个客户端都可以同步自己的调用,因此在您的示例中,ALPHA 会在调用 2 之前等待调用 1 完成 OK

这里的缺点是你需要仔细考虑你需要什么样的失败逻辑,并确保客户端和服务代码是健壮的并且行为适当。

在您的示例中,我提到 ALPHA 可以同步自己的调用,但是如果 BETA 在 ALPHA 调用操作 1 之后但在调用操作 2 之前调用操作 3,则可能是

  1. 操作 2 将失败(例如,操作 3 删除了操作 2 尝试更新的记录)在这种情况下,您需要处理该失败。
  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 术语中,这将适用于任何ConcurrencyModeInstanceContextMode

在可扩展性很重要的情况下,这种方法是最常用的方法。它的缺点是用户体验可能很差(例如,他们可能会在不知道的情况下覆盖其更改)。

直接回答原始问题

如果您肯定需要确保消息是按顺序处理的,我认为您正在寻找InstanceContextMode.Single

http://msdn.microsoft.com/en-us/library/system.servicemodel.instancecontextmode.aspx

这意味着在服务应用程序的生命周期内创建服务类的单个实例。如果您也使用ConcurrencyMode = SingleorConcurrencyMode = Reentrant这意味着您的服务一次只会处理一条消息(因为您有一个实例并且它是单线程的)并且消息将按照接收到的顺序进行处理。

如果您使用ConcurrencyMode = multiple,那么您的单个服务实例将在不同线程上同时处理多条消息,因此无法保证处理顺序。

这对性能的影响有多严重取决于每个服务调用执行所需的时间。如果您每秒获得 10 个并且每个花费 0.1 秒,那就没问题了。如果您每秒获得 20 个并且每个通常需要 0.1 秒,那么您将看到平均时间增加了 100%。

于 2012-04-27T22:14:21.930 回答