【问题标题】:Sql Server Service Broker: How to structure Conversations for a simple queue scenario?Sql Server Service Broker:如何为简单的队列场景构建对话?
【发布时间】:2010-11-18 19:40:17
【问题描述】:

我是 Sql Server Service Broker 新手,我正在尝试掌握为(看似)简单的用例设置 Service Broker 的最佳方法:我想创建一个简单的工作队列,其中一个应用程序放弃工作项目进入队列,单独的应用程序从该队列中提取工作项目并处理它们。第一个应用程序不需要从第二个应用程序获取状态消息。我希望队列存在于单个 Sql Server 实例中。

最让我困惑的是对话/对话与这种情况的关系。我知道您只能在对话/对话的上下文中发送/接收消息,但是由于两个应用程序之间没有来回的聊天,我对何时是创建新对话的正确时间感到迷茫。两个极端的选择似乎是:

  • 每次我将工作项排入队列时,我都会开始新的对话。因此,每个对话最终都会包含一条消息。
  • 在部署时,我手动创建了一个无限期对话。当需要将工作项排入队列时,我总是将其作为单个对话的一部分发送。

走这两条路会有什么后果?

另外,在第一种情况下,我似乎需要进行一些 END CONVERSATION,以便 Sql Server 能够在内部清理资源。是否有任何指导何时将这些放置在正确的位置? (或者最终依赖对话超时可能会更好?)

【问题讨论】:

标签: sql-server queue service-broker


【解决方案1】:

您应该从每个工作项开始进行各自的对话。生产者(发起者)开始对话并发送描述工作项的消息,然后提交。消费者(目标)接收消息(或被激活),检查有效负载以了解工作项详细信息,执行工作,然后结束对话并提交。生成的 EndDialog 消息被发送回发起者服务队列,发起者队列上的激活过程通过结束发起者端的对话来响应它。

这是最简单的部署,启动并运行它可以确保您有一个良好的基础。当生产者将工作项排入队列时,不要偷工减料并在发起方结束对话,这是fire-and-forget and has several draw backs

如果您有很高的性能要求(每秒超过 200 个请求),那么您必须开始更明确地管理对话。我在reusing conversations for performance reasons 上有一个博客条目。在接收端,我建议阅读Writing Service Broker Procedures

我还有一个博客条目,它几乎可以满足您的需要,尽管它不安排工作项目,而是启动一个自定义程序:Asynchronous procedure execution

如果您决定从激活的上下文中使用工作项,从而利用激活的良好自我平衡功能,那么您需要understand the EXECUTE AS context under which activation occurs

【讨论】:

  • 重复使用对话的另一个好处是您可以避免“Microsoft 内部工作请求 642585”(非公开)中描述的问题。也就是说,您创建和销毁的每个对话都会泄漏 TempDB 中分配的少量空间,直到您反弹实例后才会释放这些空间。在高吞吐量应用程序中,这可能会超出 TempDB 的大小并迫使您重新启动实例以再次缩小它。
  • 对每条消息使用单独对话的一个问题是,对话可能会在另一端出现乱序。如果您在接收端只有一个线程,甚至会发生这种情况。请记住,Service Broker 仅保证对话中的顺序。 Conversations 的全部原因是当队列的接收端有多个线程时管理排序。如果会话和消息保持顺序,它将禁止使用多个接收线程。
【解决方案2】:

我真的很喜欢 Remus 的回答,尽管它并没有特别提到为什么您可能更愿意为每个工作项单独进行对话,而不是将所有工作项放在一个对话中。与此相关的两个注释:

首先,如果您有多个线程/进程处理工作项,那么将所有工作项放在一个对话中可能会导致并发问题。 Service Broker 工作进程看起来像这样(在伪代码中):

begin transaction
receive top n work items from queue
process work items
commit transaction

(通过在成功处理工作项之前不提交,您可以确保,例如,如果您的进程死亡,那么它已收到但尚未处理的工作项不会从队列中删除。)

并发问题会出现,因为服务代理程序被编程为每个 RECEIVE 命令获取队列中与被接收消息共享相同会话(或会话组)的所有消息的独占读取锁。该锁一直保持到事务提交为止。 (参见Conversation Group Locks。)因此,如果队列中的所有工作项都在一个对话中,那么当一个工作进程处于“处理工作项”步骤时,没有其他工作进程可以做任何工作。

将大量项目放入单个对话中的第二个问题是,它会增加您在某些错误条件下可能丢失或必须重新处理的工作项目的数量。为了恰当地描述这一点,我听从 Remus 的意见。请参阅他的Recycling Conversations,尤其是“重用单个对话框发送所有消息 [...] 就像将所有鸡蛋放在一个篮子里一样”的部分。您可能能够从其中一些错误情况中恢复过来,但它可能会给您的代码带来更多的复杂性。

对于所有工作项使用单一对话可能还有一些争论,但我对它们并不熟悉。

这并不是说正确的解决方案总是为每个工作项开始单独的对话。不过,在阅读了 Remus 的帖子后,他的建议似乎很合理。从每个对话一个工作项开始,然后根据需要增加复杂性。 (但在任何情况下,您都不应该走极端将所有消息放在一个对话中。)

【讨论】:

    猜你喜欢
    • 2012-08-01
    • 2010-11-21
    • 2013-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多