Service Broker:设计两个SQL服务器之间的通信

时间:2012-11-02 15:01:11

标签: sql-server service-broker

目标:将连续生成的记录可靠地传输到中央SQL。

摘要:卫星需要向中心询问其最新数据,然后它会不断发送新数据。

卫星SQL服务器有时可能(重新)启动(与物理生产机器相关),中央SQL机器可能会运行更长时间,但也可能会有一些停机时间。这些行大多数都是可靠的,但是人们永远不会知道......我希望SQL Server Service Broker能够自然地解决连接问题 。但是,我需要解决初始握手和发送数据的问题。我需要设计协议,我的SSB知识仍然很差。

我希望我理解使用SSB进行通信的基本知识,这些基本知识简洁地描述为

  

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

......来自Remus Rusanu(如果有兴趣的话请见more details in his earlier answer)。

我想将记录发送为这样的XML消息(这里是一个多行字符串)

<row a="1" b="11" c="111" />
<row a="2" b="22" c="222" />
<row a="3" b="33" c="333" />
<row a="4" b="44" c="444" />

我已经学会了how to write the SELECT to obtain the info from the XML message

沟通:说SQL服务器之间的通信机制刚刚被激活......

  1. 卫星SQL获得了新数据,并以某种方式知道卫星和中心之间没有待处理的消息。但它也不知道哪些数据已经发送到中央。因此,它必须询问中心最新的数据是什么。

  2. 如果我正确理解END CONVERSATION,该命令会导致仅发送一种空消息N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'。因此,中心应该可以在ReplyMessage之前将回复类型END CONVERSATION发送回来。

  3. 同时,卫星应该不采取任何行动(即发送RequestMessage后等待)。 ReplyMessage到达后,它会激活以下卫星程序:

    • 收到消息并获取所需信息,
    • 接收EndDialog消息并结束卫星侧的上一个对话框
    • 准备要发送到中心的数据(XML字符串形式),
    • 打开新对话框
    • 发送准备好的数据,
    • 并让自己处于休眠状态,直到另一个ReplyMessage到来。
  4. 中央应该表现得相似。一旦RequestMessage到达,它就会激活以下中央程序:

    • 收到请求消息,
    • 提取XML信息并更新中央数据库,
    • 获取有关卫星最后可用数据的信息,
    • 形成并发送ReplyMessage
    • 结束中央对话框。
  5. 到目前为止,我的观点是否正确?

    现在有一些我不确定的细节:因为我想让它变得健壮,无需人工操作,它应该在安装机制时自行启动。通信应始终由卫星启动(即使卫星工作甚至存在,中心也可能不知道。)

    卫星已经使用由原始数据触发的触发器,该原始数据被处理以构建在中央收集的记录。这样,触发器可以以某种方式启动卫星到中央的第一个SSB请求。但...

    • 触发器如何检查卫星与中心之间是否没有待处理的通信?由于卫星始终是发起者,因此问题也可以表述为......触发器如何检查卫星是否等待某些ReplyMessage?或者,如何知道卫星和中央之间有一些开放的对话?
      • 如果没有对话,则触发器形成数据记录(存储在本地表中),然后它可以启动通信过程(参见上面的第1点)。
      • 如果有任何对话,则触发器仅形成稍后要发送的记录,而不执行任何其他操作。当激活的程序获得ReplyMessage时,将发送数据(见上文第3点)。
    • 关于让卫星休眠(参见上面第3点的最后一个内容)我的意思是消息队列中可能没有其他消息(循环中没有任何要处理的内容)和激活的程序要自然完成。但我不确定我在这里是否正确思考。你能评论一下吗?
    • 如果没有任何东西被打破,如果数据生成得足够快,那么卫星和中心总会有交换的东西。这样,触发器就不应该尝试启动通信。
    • 当没有任何其他东西通过激活的卫星程序发送到中心时(实际上,它没有激活),或者当系统以某种方式重新启动时(尚未存在对话框),触发器开始通信过程。但我怎么能这样做呢?触发器是否应该将RequestMessage发送到中央? (这样ReplyMessage将激活卫星程序,程序将继续,直到有任何事情要处理。)
    • 说,RequestMessage表示这里是一些数据,处理它们,并回复最后一个(或者下一个应该发送什么或者从中央推荐下一个行动 - - 取决于业务逻辑,这里可能不重要)。在{{1>}中发送空XML 是否可以我不知道你还需要什么 - 告诉我

    第一次更新 - 基于the Remus Rusanu's answer below

    我必须同意这是 chatty 协议。此外,记录是作为来自真实环境的温度样本创建的,并且采样频率相当低。这意味着复杂的协议只有在重启所有内容的特殊情况下才有用。

    但是当对话应该保持打开永远时,对话框句柄的唯一标识符应该永久存储在某个配置表中,或者甚至可以硬连线到代码。触发器将立即发送记录,中央将不会发送RequestMessage。这是对的吗?那种覆盖是否可以被有效地视为一种独白?

    第二次更新,使这个问题相当短。

    请参阅续篇Service Broker: How should a trigger start infinitely opened dialog?

1 个答案:

答案 0 :(得分:2)

  

我需要解决初始握手和发送数据的问题。

我无法告诉你如何阅读这个从2004年带回闪存...当时我们有设计发布 - 订阅协议('monologs'与'对话'),其中一个设计要点是要求为'检查站'。以这种方式思考:“发布者”分发项目目录。它最初将目录的内容作为 checkpoint 消息发送,然后继续将目录的任何更新(添加或删除的项目,价格更改等)作为 update 消息发送。每当有人订阅此出版物时,它需要获取检查点消息和所有后续更新消息,以便他的目录是最新的。随着时间的推移,更新列表变得非常长,因此发布者会定期将当前目录状态重新发送为“检查点”。后续订阅者只需要最后一个检查点,以及此后的任何更新。发布者基础结构也可以删除在该最后一个检查点之前发送的所有消息,因为没有订阅者将再需要它。这解决了新订户加入数据流“中间”并获得一致状态的问题。问题(和解决方案)与SQL Server本身处理日志记录和恢复的方式没有什么不同(名称'checkpoint'并非巧合......)。当然,整个pub-sub实现最终得到了罐头,而你所拥有的只是对话框,这些对话框没有提供实现我所描述的“开箱即用”的方法,这就是为什么我看到你正在尝试自己发明一个替代品...但我离题了。

我将总结您建议的消息交换模式如下:卫星需要向中心站询问该中心的当前状态(“水印”)。它以典型的请求 - 响应模式执行:BEGIN_TRAN-&gt; BEGIN_DIALOG-&gt; SEND_Request-&gt;发送方的COMMIT,激活 - > BEGIN_TRAN-&gt; RECEIVE-&gt; SEND_Response-&gt; END-&gt; COMMIT on发送方的接收方大小,激活 - > BEGIN_TRAN-&gt; RECEIVE-&gt; END-&gt; COMMIT。

一般来说,这是一个好的模式,但我不认为你想要的模式。你最终得到的是这样的:对于卫星所拥有的每一条新信息,它会问中心:'你有这个吗?'然后中心会回复'嗯,是的,我做'或'不,请发给我'。这是一个令人难以置信的繁琐的协议,涉及大量的消息被交换为最终使其从卫星到中心的每一个信息。我认为您应该长时间保持对话,将其视为沟通渠道。一旦建立,卫星将在每次有一些新信息时简单地发送数据。您不需要询问中心状态,SSB工作是确保您发送的任何内容确实到达中心,即使存在网络故障。开始对话,发送1MM消息并将其保持打开在SSB编程中完全正常。因此,3个月后再发送1MM消息。甚至在系统断开连接2周时这样做,并且只能在感恩节后实际发送消息。那是完全 SSB旨在处理的内容。问题是您的程序代码也应该准备好处理这个问题(即永远等待回复,它可能会在...... 2周内)。

关于SSB编程模型的更多信息。通常情况下,任何人都无论何时都在积极“等待”任何事情,SSB编程都是关于响应事件的。激活模型更接近现在的“功能编程”。想想node.js.绝大多数代码都依赖于激活的过程。您的代码应始终采用“开始交易” - >“收到消息”的形式 - >从数据库中读取消息所属项目的当前状态 - >决定结果 - >更新数据库状态 - >发送响应(如果有) ) - &GT; commit-&GT;退出。 onyl异常就像你拥有的触发器一样,就是在这个激活驱动程序的乒乓中注入新消息的代码。此代码(触发器)永远不应该等待它发送的任何响应。它应该发送并继续。对此触发器的SEND的任何响应都应通过激活来处理。异步,再次。想想C#的新东西等待异步。