Sql Server Service Broker - 外部激活的控制台应用程序的全面、使用中的示例

Posted

技术标签:

【中文标题】Sql Server Service Broker - 外部激活的控制台应用程序的全面、使用中的示例【英文标题】:Sql Server Service Broker - thorough, in-use example of externally activated console app 【发布时间】:2012-11-16 22:47:59 【问题描述】:

我需要任何部署了使用 Sql Server Service Broker 外部激活机制(通过 Feature Pack 中的 Service Broker External Activator)的实际生产中应用程序的人的指导。

目前的心态:

我的规格相当简单(或者至少我是这么认为的),所以我正在考虑以下基本流程:

    类似订单的实体被插入到状态为“已确认”的 Table_Orders

    SP_BeginOrder 被执行并执行以下操作:

    开始交易 从 Service_HandleOrderState 开始 DIALOG 到 Service_PreprocessOrder 将对话句柄(从现在开始PreprocessingHandle)存储在Orders表的特定列中 使用 PreprocessingHandle 发送包含订单 ID 的 Message_PreprocessOrder 类型的 MESSAGE 结束交易

    请注意,我并没有结束对话,我不想"fire-and-forget"

    Queue_PreprocessOrder 上的

    事件通知会激活 PreprocessOrder.exe 的一个实例(最大并发 1),该实例执行以下操作:

    开始一个 SqlTransaction 从 Queue_PreprocessOrder 收到前 1 条消息 如果消息类型是 Message_PreprocessOrder(格式 XML): 使用消息正文中的订单 ID 在 Table_Orders 中将订单状态设置为“预处理” 加载 n 个数据集合,其中计算 n-ary Carthesian product(通过 Linq,AFAIK,这在 T-SQL 中是不可能的)以确定订单项集合 将订单项行插入到 Table_OrderItems 使用PreprocessingHandle发送一个Message_PreprocessingDone类型的消息,包含相同的订单ID 结束与PreprocessingHandle相关的对话 提交 SqlTransaction 以 Environment.Exit(0) 退出 Queue_HandleOrderState 上的内部激活会执行一个 SP(最大并发 1): 开始交易 从 Queue_InitiatePreprocessOrder 接收前 1 条消息 如果消息类型是 Message_PreprocessingDone: 使用消息正文中的订单 ID 在 Table_Orders 中将订单状态设置为“处理中” 从 Service_HandleOrderState 开始 DIALOG 到 Service_ProcessOrderItem 将对话句柄(从现在开始ProcessOrderItemsHandle)存储在Table_Orders的特定列中 为当前订单 ID 和每一行的 Table_OrderItems 中的行创建一个游标: 使用 ProcessOrderItemsHandle 发送一个 Message_ProcessOrderItem 类型的 MESSAGE,包含订单项 ID 如果消息类型是Message_ProcessingDone: 在Table_Orders中使用消息正文中的订单ID将订单状态设置为“已处理” 如果消息类型是http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog(结束对话框): 结束与消息的对话句柄有关的对话 结束交易 Queue_ProcessOrderItem 上的事件通知会激活 ProcessOrderItem.exe 的一个实例(最大并发 1),它执行以下操作: 开始一个 SqlTransaction 从 Queue_ProcessOrderItem 收到前 1 条消息 如果消息类型是 Message_ProcessOrderItem(格式 XML): 在 Table_OrdersItems 中使用消息正文中的订单项目 ID 将订单项目状态设置为“处理中”,然后: 加载订单商品参数集合 使用参数向 URL 发出 HttpRequest 将 HttpResponse 作为 PDF 存储在文件系统上 如果上述子步骤中出现任何错误,则将订单商品状态设置为“错误”,否则设置为“正常” 在 Table_OrdersItems 中执行查找以确定是否所有订单项目都已处理(状态为“ok”或“error”) 如果所有订单项目都已处理: 使用ProcessOrderItemsHandle发送一个Message_ProcessingDone类型的消息,包含订单ID 结束与 ProcessOrderItemsHandle 相关的对话 提交 SqlTransaction 以 Environment.Exit(0) 退出

注意事项:

规范指定 MSSQL 兼容性 2005 到 2012,因此: 没有对话组 没有对话优先级 没有 POISON_MESSAGE_HANDLING(状态 = 关闭) 我正在努力实现整体流程的完整性和连续性,而不是速度 鉴于表和 SP 驻留在 DB1 中,而 Service Broker 对象(消息、合同、队列、服务)驻留在 DB2DB2值得信赖

问题:

    所描述的架构中是否存在任何重大设计缺陷? 订单完成状态跟踪似乎不正确。有没有更好的方法?也许使用 QUEUE RETENTION ? 我的直觉告诉我,在任何情况下,激活的外部 exe 都不应以 0 以外的退出代码终止,因此 Main 中应该有 try..catch(Exception e).. finally Environment.Exit(0) 。这个假设正确吗? 您将如何组织 DB 代码中的错误处理?错误日志表就够了吗? 您将如何组织外部 exe C# 代码中的错误处理?相同的错误记录 桌子? 我见过SQL Server Service Broker Product Samples,但对于我看似简单的案例,Service Broker 接口似乎有点过头了。更简单的 Service Broker 对象模型的任何替代方案? 任何用于 Service Broker 的跨版本“便携式”管理工具至少能够排出有害消息? 对于上述任何一个,你有什么像样的代码示例吗?

【问题讨论】:

【参考方案1】:

问:所描述的架构中是否存在任何重大设计缺陷? 答:几个小福利: - 在保持事务打开的同时等待 HTTP 请求完成是不好的。无论如何,您无法在数据库和 HTTP 之间实现事务一致性,因此当 HTTP 速度较慢时,不要冒险让事务延长几分钟。典型的模式是 begin tran/receive/begin conversation timer/commit 然后发出没有任何 DB xact 的 HTTP 调用。如果 HTTP 调用成功,则 begin xact/send response/end conversation/commit。如果 HTTP 失败(或客户端崩溃),那么让会话时间再次激活您。您将收到一条计时器消息(无正文),您需要从表中获取与句柄关联的项目 ID。

问:订单完成状态跟踪似乎不正确。有没有更好的方法?也许使用 QUEUE RETENTION ? 答:我对您的状态跟踪的一个批评是依赖于扫描订单项目以确定当前处理的项目是最后一个(5.3.4)。例如,您可以在项目状态中添加这是要处理的“最后一个”项目的信息,以便在处理它时知道需要报告完成。 RETENTION 仅在调试时有用,或者当您有需要运行“逻辑回滚”和补偿对话错误操作的逻辑时。

问:我的直觉告诉我,在任何情况下,激活的外部 exe 都不应该以 0 以外的退出代码终止,所以最后应该有 try..catch(Exception e).. Environment.Exit(0) 在 Main。这个假设正确吗? A:最重要的是被激活的进程在队列上发出RECEIVE语句。如果它没有这样做,队列监视器may enter the notified state forever。如果我没记错的话,退出代码是无关紧要的。与任何后台进程一样,捕获和记录异常都很重要,否则当它开始失败时,您甚至永远不会知道它有问题。除了规范的 try/catch 块之外,连接 Application.ThreadException 用于 UI 应用程序和 AppDomain.UnhandledException 用于 UI 和非 UI 应用程序。

问:您将如何组织 DB 代码中的错误处理?错误日志表就够了吗? 答:我稍后会跟进。错误日志表就足够了。

问:您将如何组织外部 exe C# 代码中的错误处理?相同的错误记录表? 答:我创建bugcollect.com 正是因为我必须用自己的应用程序处理此类问题。问题不仅仅是日志记录,您还需要一些聚合和分析(至少检测重复报告)并抑制“现场”某些部署配置事故造成的错误泛滥。说实话,现在有更多的选择,例如。 exceptron.com。当然,我认为 FogBugs 也有日志记录功能。

问:我已经看过 SQL Server Service Broker 产品示例,但对于我看似简单的案例,Service Broker 接口似乎有点过头了。更简单的 Service Broker 对象模型的任何替代方案? 最后,一个简单的问题:是的,这太过分了。没有简单的模型。

问:Service Broker 的任何跨版本“便携式”管理工具至少能够排出有害消息吗? A:毒消息的问题是毒消息的定义随着你的代码而改变:the poison message is whatever message breaks the current guards set in place to detect it。

问:对于上述任何一个,你有什么像样的代码示例吗? 答:没有

还有一点:尽量避免从 DB1 到 DB2 的任何引用(例如,在 DB1 中激活了 4.3.4 并从 DB2 读取项目表)。这会创建跨数据库依赖关系,当 a) 一个数据库脱机(例如,为了维护)或过载或 b) 您为 HA/DR 添加数据库镜像并且一个数据库故障转移时,这些依赖关系会中断。即使 DB1 和 DB2 在不同的机器上(并且没有链接的服务器),也要尝试让代码工作。如有必要,向消息有效负载添加更多信息。如果您以这样的方式构建它,DB2 可以在不同的机器上,甚至可以存在多台 DB2 机器来扩展 HTTP/PDF 编写工作。

最后:这个设计会非常慢。我说的是每秒几十条消息很慢,涉及的对话/消息太多,所有内容都与 max_queue_readers 1。这对你来说可能接受也可能不接受。

【讨论】:

Q1: 那么当ProcessOrderItem.exe 收到DialogTimer 消息时,它必须始终执行BEGIN CONVERSATION TIMER,然后重试HTTP 请求? Q2:您建议在订单商品上设置“IsLastItem”标志。这不会与 DialogTimer 消息冲突,因为 ProcessOrderItem.exe 会在收到 DialogTimer 之前到达最后一个订单项并将订单状态设置为“已处理” 消息? Q7:我指的是类似于SSB Admin Tool 的内容。我找不到它的来源,所以无法修改跨版本兼容 Q1:是的,总是安排重试,提交,然后做工作。这是 100% 的故障安全,不会留下任何竞争条件,系统可以在没有安排重试计时器的情况下保留。您可能会考虑一些花哨的东西,例如指数退避和总时间限制(例如,如果每次重试在 1 天后失败,则放弃),但简单并不一定意味着不好;)

以上是关于Sql Server Service Broker - 外部激活的控制台应用程序的全面、使用中的示例的主要内容,如果未能解决你的问题,请参考以下文章

sql server Service Broker 相关查询

SQL Server Service Broker 的缺点 [关闭]

错误:未启用当前数据库的SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker。

未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker。]

未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker

The SQL Server Service Broker for the current database is not enabled