導讀:本文主要涉及Service Broker的基本概念及建立一個Service Broker應用程序的基本步驟。
一、前言:
Service Broker為SQL Server提供消息隊列,這提供了從數據庫中發送異步事務性消息隊列的方法。Service Broker消息可以保證以適當的順序或原始的發送順序不重復地一次性接收。並且因為內建在SQL Server中,這些消息在數據庫發生故障時是可以恢復的,也可以隨數據庫一起備份。在SQL Server 2008中,還引入了使用Create Broker Priority命令對會話設定優先級,可以對重要的或不重要的會話進行優先級設定,以保證消息合理地處理。
本文假定一個在線數據庫BookStore中存儲了一些業務訂單。我們使用Service Broker應用程序將消息發送到另一個數據庫BookDistribution,該數據庫是分離的應用程序調用,該應用程序控制倉庫入庫和出庫交付, 並返回消息給BookStore。
創建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_endpoints
三、小結
本文通過一個實例演示了一個用來發送圖書訂單消息分發控制數據庫的簡單的消息交換應用程序。發起方發送圖書訂單,發回一個響應,並在兩個數據庫上使用END Conservation結束會話。現實場景中可以轉換為其他消息類型、契約、服務和隊列。合理運用Service Broker應用程序的異步特性可以防止因應用程序掛起而導致業務系統產生瓶頸。
本文參考:
1、SQL Server 2005 Service Broker 初探
http://msdn.microsoft.com/zh-cn/library/ms345108%28v=sql.90%29.ASPx
2、SQL Server 2008 Transact-SQL Recipes: A Problem-Solution Approach
http://www.amazon.com/Server-2008-Transact-SQL-Recipes-Problem-Solution/dp/1590599802