建议大家先看微软官方的有关BROKER资料。备着以后使用。
http://msdn.microsoft.com/zh-cn/library/ms345108(SQL.90).aspx
然后再看下面的案例。因为下面的案例就是基于该上面的资料演示的。否则很难直接看懂该案例
同时谢谢该案例的作者 http://www.cnblogs.com/downmoon/archive/2011/04/05/2005900.html
创建Service Broker应用程序大体步骤如下:
1、定义希望应用程序执行的异步任务。
2、确定Service Broker的发起方服务和目标服务是否创建在同一个SQL Server实例中。如果是两个实例,实例间的通信还需要创建经过证书认证或NT安全的身份认证,并且要创建端点、路由以及对话安全模式。
3、如果没有启用,则在多方参与的数据库中使用Alter Database命令设置Enable_broker以及Truseworthy数据库选项。
4、为所有多方参与的数据库创建数据库主密钥。
5、创建希望在服务之间发送的消息类型。
6、创建契约(Contract)来定义可以由发起方发送的各种消息以及由目标发送的消息类型的种类。
7、同时在两方参与的数据库中创建用于保存消息的队列。
8、同时在绑定特定约定到特定队列的多方参与的数据库中创建服务。
二、实例
下面我们通过一个示例来实现以上步骤:
(一)、启用数据库的Service Broker活动
-- Enabling Databases for Service Broker Activity USE master GO IF NOT EXISTS ( SELECT name FROM sys.databases WHERE name = ' BookStore ' ) CREATE DATABASE BookStore GO IF NOT EXISTS ( SELECT name FROM sys.databases WHERE name = ' BookDistribution ' ) CREATE DATABASE BookDistribution GO ALTER DATABASE BookStore SET ENABLE_BROKER GO ALTER DATABASE BookStore SET TRUSTWORTHY ON GO ALTER DATABASE BookDistribution SET ENABLE_BROKER GO ALTER DATABASE BookDistribution SET TRUSTWORTHY ON
(二)、创建数据库主密钥
-- Creating the DatabaseMaster Key for Encryption USE BookStore GO CREATE MASTER KEY ENCRYPTION BY PASSWORD = ' I5Q7w1d3 ' GO USE BookDistribution GO CREATE MASTER KEY ENCRYPTION BY PASSWORD = ' D1J3q5z8X6y4 ' GO
(三)、管理消息类型
使用CREATE MESSAGE TYPE(http://msdn.microsoft.com/en-us/library/ms187744.aspx)命令,
-- Managing Message Types Use BookStore GO -- 发送图书订单的消息类型 CREATE MESSAGE TYPE [ //SackConsulting/SendBookOrder ] VALIDATION = WELL_FORMED_XML GO -- 目标数据库发送的消息类型 CREATE MESSAGE TYPE [ //SackConsulting/BookOrderReceived ] VALIDATION = WELL_FORMED_XML GO -- 执行同样的定义 Use BookDistribution GO -- 发送图书订单的消息类型 CREATE MESSAGE TYPE [ //SackConsulting/SendBookOrder ] VALIDATION = WELL_FORMED_XML GO -- 目标数据库发送的消息类型 CREATE MESSAGE TYPE [ //SackConsulting/BookOrderReceived ] VALIDATION = WELL_FORMED_XML GO
--注意,此处没有定义消息的内容。实际的消息是消息类型的实例。
(四)、创建契约(Contract)
使用Create Contract(http://msdn.microsoft.com/en-us/library/ms178528.aspx)
-- Creating Contracts Use BookStore GO CREATE CONTRACT [ //SackConsulting/BookOrderContract ] ( [ //SackConsulting/SendBookOrder ] SENT BY INITIATOR, [ //SackConsulting/BookOrderReceived ] SENT BY TARGET ) GO USE BookDistribution GO CREATE CONTRACT [ //SackConsulting/BookOrderContract ] ( [ //SackConsulting/SendBookOrder ] SENT BY INITIATOR, [ //SackConsulting/BookOrderReceived ] SENT BY TARGET ) GO
--发起方和目标的定义必须相同
(五)、创建队列
队列用来保存数据。使用命令Create queue(http://msdn.microsoft.com/en-us/library/ms190495.aspx)
-- Creating Queues Use BookStore GO -- 保存BookDistribution过来的消息 CREATE QUEUE BookStoreQueue WITH STATUS = ON GO USE BookDistribution GO -- 保存BookStore过来的消息 CREATE QUEUE BookDistributionQueue WITH STATUS = ON GO
(六)、创建服务
服务定义端点,然后使用它来将消息队列绑定到一个或多个契约上。服务使用队列和契约来定义一个或一组任务。有点拗口,是不是?
服务是消息的发起方和接收方强制约定的规则,并将消息路由到正确的序列。
使用Create Service(http://msdn.microsoft.com/en-us/library/ms190332.aspx)命令。
-- Creating Services Use BookStore GO CREATE SERVICE [ //SackConsulting/BookOrderService ] ON QUEUE dbo.BookStoreQueue -- 指定的队列绑定到契约 ( [ //SackConsulting/BookOrderContract ] ) GO USE BookDistribution GO CREATE SERVICE [ //SackConsulting/BookDistributionService ] ON QUEUE dbo.BookDistributionQueue -- 指定的队列绑定到契约 ( [ //SackConsulting/BookOrderContract ] ) GO
(七)、启动对话
对话会话(dialog conservation)是在服务之间进行消息交换的操作。
使用Begin Dialog Conversation(http://msdn.microsoft.com/en-us/library/ms187377.aspx) 命令创建新的会话。使用Send(http://msdn.microsoft.com/en-us/library/ms188407.aspx)来发送消息。使用End Conversation命令(http://msdn.microsoft.com/en-us/library/ms177521.aspx)结束会话。
-- Initiating a Dialog Use BookStore GO -- 保存会话句柄和订单信息 DECLARE @Conv_Handler uniqueidentifier DECLARE @OrderMsg xml; BEGIN DIALOG CONVERSATION @Conv_Handler -- 创建会话 FROM SERVICE [ //SackConsulting/BookOrderService ] TO SERVICE ' //SackConsulting/BookDistributionService ' ON CONTRACT [ //SackConsulting/BookOrderContract ] ; SET @OrderMsg = ' <order id="3439" customer="22" orderdate="2/15/2011"> <LineItem ItemNumber="1" ISBN="1-59059-592-0" Quantity="1" /> </order> ' ; SEND ON CONVERSATION @Conv_Handler -- 发送到BookDistribution数据库的队列中 MESSAGE TYPE [ //SackConsulting/SendBookOrder ] ( @OrderMsg );
(八)、查询队列中传入的消息
-- Querying the Queue for IncomingMessages USE BookDistribution GO SELECT message_type_name, CAST (message_body as xml) message, queuing_order, conversation_handle, conversation_group_id FROM dbo.BookDistributionQueue
(九)、检索并响应消息
使用Receive语句(http://msdn.microsoft.com/en-us/library/ms186963.aspx)从队列中读取行(消息),也可以删除已经读取的消息。Receive的结果可以填充到常规表中,也可以在局部变量中执行其他操作,或发送到其他service Broker消息。如果消息是XML数据类型的消息,则可以直接借助TSQL的XQuery来操作。
-- Receiving and Responding to aMessage USE BookDistribution GO -- 创建一个表存放接收到的订单信息 CREATE TABLE dbo.BookOrderReceived (BookOrderReceivedID int IDENTITY ( 1 , 1 ) NOT NULL , conversation_handle uniqueidentifier NOT NULL , conversation_group_id uniqueidentifier NOT NULL , message_body xml NOT NULL ) GO -- 声明变量 DECLARE @Conv_Handler uniqueidentifier DECLARE @Conv_Group uniqueidentifier DECLARE @OrderMsg xml DECLARE @TextResponseMsg varchar ( 8000 ) DECLARE @ResponseMsg xml DECLARE @OrderID int ; -- 从队列中获取消息,将接收值赋于局部变量 RECEIVE TOP ( 1 ) @OrderMsg = message_body, -- TOP指定最多一条消息 @Conv_Handler = conversation_handle, @Conv_Group = conversation_group_id FROM dbo.BookDistributionQueue; -- 将变量值插入表中 INSERT dbo.BookOrderReceived (conversation_handle, conversation_group_id, message_body) VALUES ( @Conv_Handler , @Conv_Group , @OrderMsg ) -- 使用XQuery进行抽取以响应消息订单 SELECT @OrderID = @OrderMsg .value( ' (/order/@id)[1] ' , ' int ' ) SELECT @TextResponseMsg = ' <orderreceived id= " ' + CAST ( @OrderID as varchar ( 10 )) + ' "/> ' ; SELECT @ResponseMsg = CAST ( @TextResponseMsg as xml); -- 使用既有的会话句柄,发送响应消息到发起方 SEND ON CONVERSATION @Conv_Handler MESSAGE TYPE [ //SackConsulting/BookOrderReceived ]
(十)、结束会话
-- Ending a Conversation USE BookStore GO -- 创建订单确认表 CREATE TABLE dbo.BookOrderConfirmation (BookOrderConfirmationID int IDENTITY ( 1 , 1 ) NOT NULL , conversation_handle uniqueidentifier NOT NULL , DateReceived datetime NOT NULL DEFAULT GETDATE (), message_body xml NOT NULL ) DECLARE @Conv_Handler uniqueidentifier DECLARE @Conv_Group uniqueidentifier DECLARE @OrderMsg xml DECLARE @TextResponseMsg varchar ( 8000 ); RECEIVE TOP ( 1 ) @Conv_Handler = conversation_handle, @OrderMsg = message_body FROM dbo.BookStoreQueue INSERT dbo.BookOrderConfirmation (conversation_handle, message_body) VALUES ( @Conv_Handler , @OrderMsg ); END CONVERSATION @Conv_Handler ; GO USE BookDistribution GO DECLARE @Conv_Handler uniqueidentifier DECLARE @Conv_Group uniqueidentifier DECLARE @OrderMsg xml DECLARE @message_type_name nvarchar ( 256 ); RECEIVE TOP ( 1 ) @Conv_Handler = conversation_handle, @OrderMsg = message_body, @message_type_name = message_type_name FROM dbo.BookDistributionQueue -- 双方必须都结束会话 IF @message_type_name = ' http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog ' BEGIN END CONVERSATION @Conv_Handler ; END
--查询会话状态
SELECT state_desc, conversation_handle FROM sys.conversation_endpointsService Broker的设置会话优先级自SQL Server 2008起,对非常活跃的Service Broker应用程序,提供了设置优先级的命令CREATE BROKER PRIORITY(http://msdn.microsoft.com/en-us/library/bb934170.aspx)。通过该命令,可以设置从1至10共10个等级的颗粒度来调试会话的优先级,默认为5。在此之前,你必须得首先打开HONOR_BROKER_PRIORITY开关。
-- 设置会话优先级 -- 启用会话优先级选项 ALTER DATABASE BookStore SET HONOR_BROKER_PRIORITY ON -- 启用会话优先级选项 ALTER DATABASE BOOKDistribution SET HONOR_BROKER_PRIORITY ON -- 查看设置结果 SELECT name, is_honor_broker_priority_on FROM sys.databases WHERE name IN ( ' BookStore ' , ' BookDistribution ' ) /* name is_honor_broker_priority_on BookStore 1 BookDistribution 1 */ USE BookStore GO CREATE BROKER PRIORITY Conv_Priority_BookOrderContract_BookOrderService FOR CONVERSATION SET (CONTRACT_NAME = [ //SackConsulting/BookOrderContract ] , -- 特定的契约 LOCAL_SERVICE_NAME = [ //SackConsulting/BookOrderService ] , -- 本地服务 REMOTE_SERVICE_NAME = ANY , -- 远程服务为ANY,即Service Broker端点的任何相关服务 PRIORITY_LEVEL = 10 ) -- 设置优先级为10通过sys.conversation_priorities目录视图,查询优先级
如果你希望包含服务和契约名称,可以将服务和从sys.conversation_priorities( http://msdn.microsoft.com/zh-cn/library/bb895280%28v=sql.100%29.aspx)返回的契约ID与sys.service_contracts( http://msdn.microsoft.com/en-us/library/ms184378.aspx),sys.services( http://msdn.microsoft.com/en-us/library/ms174429.aspx)目录视图关联起来。SELECT name, priority, service_contract_id, local_service_id,remote_service_name FROM sys.conversation_priorities cp /* name priority service_contract_id local_service_id remote_service_name Conv_Priority_BookOrderContract_BookOrderService 10 65536 65536 NULL */USE BookDistribution GO -- 创建目标服务的优先级,与发起方的优先级保持一致, -- 以使会话的优先级设置覆盖整个会话的生命周期 CREATE BROKER PRIORITY Conv_Priority_BookOrderContract_BookDistributionService FOR CONVERSATION SET (CONTRACT_NAME = [ //SackConsulting/BookOrderContract ] , LOCAL_SERVICE_NAME = [ //SackConsulting/BookDistributionService ] , REMOTE_SERVICE_NAME = ANY , PRIORITY_LEVEL = 10 ) USE BookStore GO ALTER BROKER PRIORITY Conv_Priority_BookOrderContract_BookOrderService FOR CONVERSATION SET (REMOTE_SERVICE_NAME = ' //SackConsulting/BookDistributionService ' ) -- 修改远程服务名称 ALTER BROKER PRIORITY Conv_Priority_BookOrderContract_BookOrderService FOR CONVERSATION SET (PRIORITY_LEVEL = 9 ) -- 设置优先级 -- 删除优先级设置 DROP BROKER PRIORITY Conv_Priority_BookOrderContract_BookOrderService二、Service Broker的存储过程实现
在上文中,我们使用的临时T-SQL来演示Service broker的步骤,事实上, 我们完全可以通过存储过程或外部应用程序自动激活并处理队列中的消息。使用Create Queue(http://msdn.microsoft.com/en-us/library/ms190495.aspx)和Alter Queue(http://msdn.microsoft.com/en-us/library/ms189529.aspx)选项,也可以指定可以激活并处理在同一队列中传入的消息的、同时执行的相同服务程序的数量。
继续上文的示例:
-- Creating the Bookstore Stored Procedure USE BookDistribution GO CREATE PROCEDURE dbo.usp_SB_ReceiveOrders AS DECLARE @Conv_Handler uniqueidentifier DECLARE @Conv_Group uniqueidentifier DECLARE @OrderMsg xml DECLARE @TextResponseMsg varchar ( 8000 ) DECLARE @ResponseMsg xml DECLARE @Message_Type_Name nvarchar ( 256 ); DECLARE @OrderID int ; -- XACT_ABORT automatically rolls back the transaction when a runtime error occurs SET XACT_ABORT ON BEGIN TRAN ; RECEIVE TOP ( 1 ) @OrderMsg = message_body, @Conv_Handler = conversation_handle, @Conv_Group = conversation_group_id, @Message_Type_Name = message_type_name FROM dbo.BookDistributionQueue; IF @Message_Type_Name = ' //SackConsulting/SendBookOrder ' BEGIN INSERT dbo.BookOrderReceived (conversation_handle, conversation_group_id, message_body) VALUES ( @Conv_Handler , @Conv_Group , @OrderMsg ) SELECT @OrderID = @OrderMsg .value( ' (/order/@id)[1] ' , ' int ' ) SELECT @TextResponseMsg = ' <orderreceived id= " ' + CAST ( @OrderID as varchar ( 10 )) + ' "/> ' ; SELECT @ResponseMsg = CAST ( @TextResponseMsg as xml); SEND ON CONVERSATION @Conv_Handler MESSAGE TYPE [ //SackConsulting/BookOrderReceived ] ( @ResponseMsg ); END IF @Message_Type_Name = ' http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog ' BEGIN END CONVERSATION @Conv_Handler ; END COMMIT TRAN GO解析:该存储过程包含处理//SackConsulting/SendBookOrder和http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog消息类型的逻辑。如果发送发后者,特定会话的句柄的特定会话会结束。如果接收到图书订单消息类型,它的消息将插入到表中,并且返回订单确认信息。
可以使用Alter Queue命令修改既有的队列。这个命令使用与Create Queue相同的选项,它允许改变队列的状态与保持期、待激活的存储过程、队列读取存储过程实例的最大数量以及过程的安全模式契约。
Alter Queue包括一个额外的参数Drop,它用来删除队列的所有存储过程激活设置。
使用Alter Queue命令将存储过程绑定到既有的队列:
-- --使用Alter Queue命令将存储过程绑定到既有的队列 ALTER QUEUE dbo.BookDistributionQueue WITH ACTIVATION (STATUS = ON , PROCEDURE_NAME = dbo.usp_SB_ReceiveOrders, MAX_QUEUE_READERS = 2 , -- 独立处理队列中不同消息的同一存储过程同时执行的最大数量 EXECUTE AS SELF) -- 即存储过程将以与执行Alter Queue命令的主体的相同的权限来执行为了测试BookStore数据库的新服务程序,开始一个会话并设置新顺序:
Use BookStore GO DECLARE @Conv_Handler uniqueidentifier DECLARE @OrderMsg xml; BEGIN DIALOG CONVERSATION @conv_handler FROM SERVICE [ //SackConsulting/BookOrderService ] TO SERVICE ' //SackConsulting/BookDistributionService ' ON CONTRACT [ //SackConsulting/BookOrderContract ] ; SET @OrderMsg = ' <order id="3490" customer="29" orderdate="7/22/2008"> <LineItem ItemNumber="1" ISBN="1-59059-592-0" Quantity="2" /> </order> ' ; SEND ON CONVERSATION @Conv_Handler MESSAGE TYPE [ //SackConsulting/SendBookOrder ] ( @OrderMsg );当队列Status=ON并且队列中到到达新消息时,执行存储过程来处理传入的消息。可以使用存储过程或外部程序,但使用存储过程的好处是,它们提供了处理消息、自动执行所有需要的响应和相关业务任务的简单的封装好的组件。
如果在目标队列上有存储过程被执行,并且激活新的接收到的消息,那么应该已经有订单确认消息返回到dbo.BookStoreQueue:
SELECT conversation_handle, CAST (message_body as xml) message FROM dbo.BookStoreQueue /* conversation_handle message A7B7FA73-5B5F-E011-8B4E-001C23FA56DD <orderreceived id="3439" /> */