扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
来源: 2007年03月02日
关键字:SQL Server SQL Server 2005 SQL Server 2005 SQLServer Tim Chapman
在前面的文章中,我说明了基于消息的系统背后的理论,并介绍了Service Broker组件——在数据库引擎中建立异步消息应用程序的SQL Server 2005新特性。在本文中,我将带领你建立一个小型的Service Broker应用程序,它使用内部激活(Internal Activation)来处理所提交的消息。
内部激活
在举例之前,我想提及一下Service Broker应用程序中的内部激活功能。在许多情况下,消息一到达队列就对它们进行处理,这种做法是合适的。在Service Broker应用程序中,你可以指定一个存储过程,只要有消息到达一个队列,就立即执行它。在某种程度上,它就像队列中的异步触发器。
使用这种技巧的优点在于,如果你的队列由于收到大量消息而陷入困境,你可以对队列进行设置,使它启动更多存储过程实例。我认为这是Service Broker应用程序的一项非常重要的功能,因此我将在下面的例子中分析内部激活的运行机制。
实例
这个例子说明如何调用一个提交Service Broker消息的存储过程,它反过来调用一个存储过程来处理第一个存储过程提交的消息。
USE master
GO
IF EXISTS(SELECT * FROM sys.databases where name = 'SB')
DROP DATABASE SB
GO
CREATE DATABASE SB
GO
ALTER DATABASE SB
SET ENABLE_BROKER
GO
上面的脚本用来建立这个例子使用的数据库环境。在我能够应用其功能之前,我首先必须在数据库中启动Service Broker;ENABLE_BROKER语句帮助我启动它。
下面的脚本建立一个Sales表,我在整个实例中都要用到它。
USE SB;
GO
CREATE TABLE Sales
(
SaleID INT IDENTITY(1,1),
SaleDate SMALLDATETIME,
SaleAmount MONEY,
ItemsSold INT
);
GO
为建立所需的Service Broker组件,我创建了MESSAGE TYPE和CONTRACT对象。MESSAGE TYPE对象确认消息的内容,它对会话获得的消息实行严格控制。CONTRACT对象指定所用到的MESSAGE TYPE对象,以及会话中消息的传送方向。
CREATE MESSAGE TYPE [RecordSale] VALIDATION = NONE;
CREATE CONTRACT [SalesContract]
(
[RecordSale] SENT BY INITIATOR
);
GO
这个应用程序将消息从一个Service Broker队列传递给另一个队列,因此我需要建立实现这些功能的脚本。首先,我建立SalesQueue,它将接收消息,并启动一个过程来处理它们。(SalesService处理进入的消息并把它们传递给SalesQueue。)
CREATE QUEUE [SalesQueue];
CREATE SERVICE [SalesService] ON QUEUE [SalesQueue]([SalesContract]);
GO
点击查看更多数据库技术文章
http://soft.zdnet.com.cn/software_zone/db.shtml
列表A中的脚本建立我用作激活过程的存储过程。每次有消息到达SalesQueue队列时,就调用这个过程。
在这个过程中,我使用新的TSQL结构RECEIVE从SalesQueue队列中提取消息。(RECEIVE非常类似于SELECT语句,不同之处在于RECEIVE从队列中提取消息。)如果你需要查看消息的内容,但并不移动它们,你可以在队列上运行SELECT语句,就像在表中一样。
当我从队列收到一条消息时,我把来自队列的域值保存在当地变量中。我对@Message变更特别感兴趣,它将保存我提交到队列的XML文件。因为消息的正文以XML保存,我可以使用XQuery语句从XML文件中提取数据。我提取的数据是我提交到最初过程的变量。一旦我“切碎”了XML数据,我只需简单把这些值插入Sales表中。
既然我计划给SalesService服务发送消息,我需要一个发送消息的服务和队列。下面的语句建立RecordSalesQueue和RecordSalesService队列,后者被附加到RecordSalesQueue上。
CREATE QUEUE [RecordSalesQueue];
CREATE SERVICE [RecordSalesService] ON QUEUE [RecordSalesQueue];
GO
当你对一个Service Broker队列使用内部激活时,你需要启动内部激活并指定你将调用的存储过程。我最初建立SalesQueue时没有启动它,因此现在我必须启动它。这个操作用ALTER QUEUE语句来完成,如下:
ALTER QUEUE [SalesQueue] WITH ACTIVATION
(
STATUS = ON,
MAX_QUEUE_READERS = 1,
PROCEDURE_NAME = usp_RecordSaleMessage,
EXECUTE AS OWNER
);
GO
列表B中的脚本建立我在输入销售信息时将要用到的过程。我将保持简化,因此我仅向过程提交三个变量(尽管这不是一个很大的交易,但如有必要,可以提交更多变量)。
在这个过程中,你会注意到,我把提交到过程的参数插入到一个临时表中;然后我查询临时表,把结果集放到一个XML变量中。这种方法可以方便地把你的数据格式化成XML,而不用动态建立一个XML字符串。
实际上,BEGIN DIALOG CONVERSATION处理从RecordSalesService向SalesService提交消息的过程。这个语句返回一个会话句柄,你可以使用它来发送消息。SEND ON CONVERSATION语句执行发送XML消息的工作,SELECT语句从临时表中建立这个XML消息。
现在,我已经为激活过程做好准备,只等消息到达。要看它如何运行,执行下面的存储过程:
EXECUTE usp_SendSalesInfo '1/9/2005',30,90
执行这个过程后,运行下面的SELECT语句看Sales表中是否插入一条记录。
SELECT * FROM Sales;
内部激活的优点
我可能会看着上面的例子,心里想:“这有什么好处?不过是往表中增加一条记录而已!”在这种情况下,这是一个合理的问题。但是,不要遗漏这个例子背后的理念。
消息应用程序的目的是帮助你发送一条消息,并继续进行你的工作。在上面的例子中,如果从触发器中调用usp_SendSalesInfo存储过程,就需要在触发器下次激活前插入记录。如果触发器要完成许多处理工作,这肯定会造成系统瓶颈。但是,如果你只使用存储过程向队列传送消息,触发器就可以迅速地完成处理工作,而且可以在后台完成这些工作。
Tim Chapman是肯塔基州路易维尔市一家银行的SQL Server数据库管理员,他有超过7年的IT行业经验。他还通过了微软SQL Server 2000和SQL Server 2005的认证。
责任编辑:德东
CREATE PROCEDURE usp_RecordSaleMessage
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Handle UNIQUEIDENTIFIER;
DECLARE @MessageType SYSNAME;
DECLARE @Message XML
DECLARE @SaleDate DATETIME
DECLARE @SaleAmount MONEY
DECLARE @ItemsSold INT;
RECEIVE TOP (1)
@Handle = conversation_handle,
@MessageType = message_type_name,
@Message = message_body
FROM [SalesQueue];
IF(@Handle IS NOT NULL AND @Message IS NOT NULL)
BEGIN
SELECT @SaleDate = CAST(CAST(@Message.query('/Params/SaleDate/text()') AS NVARCHAR(MAX)) AS DATETIME)
SELECT @SaleAmount = CAST(CAST(@Message.query('/Params/SaleAmount/text()') AS NVARCHAR(MAX)) AS MONEY)
SELECT @ItemsSold = CAST(CAST(@Message.query('/Params/ItemsSold/text()') AS NVARCHAR(MAX)) AS INT)
INSERT INTO Sales(SaleDate ,SaleAmount ,ItemsSold )
VALUES(@SaleDate,@SaleAmount,@ItemsSold);
END
END
GO
CREATE PROCEDURE usp_SendSalesInfo
(
@SaleDate SMALLDATETIME,
@SaleAmount MONEY,
@ItemsSold INT
)
AS
BEGIN
DECLARE @MessageBody XML
CREATE TABLE #ProcParams
(
SaleDate SMALLDATETIME,
SaleAmount MONEY,
ItemsSold INT
)
INSERT INTO #ProcParams(SaleDate,SaleAmount, ItemsSold)
VALUES(@SaleDate, @SaleAmount, @ItemsSold)
SELECT @MessageBody = (SELECT * FROM #ProcParams FOR XML PATH ('Params'), TYPE);
DECLARE @Handle UNIQUEIDENTIFIER;
BEGIN DIALOG CONVERSATION @Handle
FROM SERVICE [RecordSalesService]
TO SERVICE 'SalesService'
ON CONTRACT [SalesContract]
WITH ENCRYPTION = OFF;
SEND ON CONVERSATION @Handle
MESSAGE TYPE [RecordSale](@MessageBody);
END
GO
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。