一般来说,在SQL Server中调用存储过程,是同步的。如果一个操作比较长,那么我们我们希望执行异步操作。
消息队列概念 。消息队列在SQL Server李,是一种存储消息的结构。消息生产者将消息发送到队列中,而消息消费者则从队列中读取并处理消息。这种机制实现了应用程序组件之间的异步通信,提高了系统的可扩展性和响应能力。
消息队列位置
接下来,我们会在同一个数据库间完成对话。
步骤
1. 启用Server Broker;请确保数据库支持Server Broker (SQL Server 2008r2及其以上,请参考官网)。https://learn.microsoft.com/zh-cn/sql/database-engine/service-broker/lesson-1-creating-the-conversation-objects?view=sql-server-ver16
ALTER DATABASE AdventureWorks2008R2SET ENABLE_BROKER;
2. 创建消息类型。复制以下代码并将其粘贴到查询编辑器窗口中。 然后,运行代码为会话创建消息类型。 由于通常在数据库引擎的多个实例间引用 Service Broker 对象,因而大多数 Service Broker 对象的名称都是以 URI 格式指定的。 这有助于确保它们在多台计算机上是唯一的。 这两种消息类型都指定 Service Broker 将只验证消息是否是格式正确的 XML 文档,并且指定 Service Broker 将不按照特定架构验证 XM
1 CREATE MESSAGE TYPE 2 [//AWDB/1DBSample/RequestMessage] 3 VALIDATION = WELL_FORMED_XML; 4 CREATE MESSAGE TYPE 5 [//AWDB/1DBSample/ReplyMessage] 6 VALIDATION = WELL_FORMED_XML; 7 GO
3.创建协定。复制以下代码并将其粘贴到查询编辑器窗口中。 然后,运行代码为会话创建约定。 约定指定了使用此约定的会话必须将类型为 /AWDB/1DBSample/RequestMessage 的消息从发起方发送到目标以及将类型为 //AWDB/1DBSample/ReplyMessage 的消息从目标发送到发起方。
1 CREATE CONTRACT [//AWDB/1DBSample/SampleContract] 2 ([//AWDB/1DBSample/RequestMessage] 3 SENT BY INITIATOR, 4 [//AWDB/1DBSample/ReplyMessage] 5 SENT BY TARGET 6 ); 7 GO
4.创建目标队列和服务。复制以下代码并将其粘贴到查询编辑器窗口中。 然后,运行代码以创建要用于目标的队列和服务。 由于队列是从相同的数据库以类似于表和视图的方式引用的,因而队列名称的格式也类似于表名称或视图名称。 CREATE SERVICE 语句将服务与 TargetQueue1DB 相关联。 因此,所有发送给此服务的消息将接收到 TargetQueue1DB 中。 CREATE SERVICE 还指定只有使用先前创建的 //AWDB/1DBSample/SampleContract 的会话才能将该服务用作目标服务。
1 CREATE QUEUE TargetQueue1DB; 2 3 CREATE SERVICE 4 [//AWDB/1DBSample/TargetService] 5 ON QUEUE TargetQueue1DB 6 ([//AWDB/1DBSample/SampleContract]); 7 GO
5.创建发起方队列和服务。复制以下代码并将其粘贴到查询编辑器窗口中。 然后,运行代码以创建要用于发起方的队列和服务。 由于未指定约定名称,因而其他服务不可将此服务用作目标服务。
1 CREATE QUEUE InitiatorQueue1DB; 2 3 CREATE SERVICE 4 [//AWDB/1DBSample/InitiatorService] 5 ON QUEUE InitiatorQueue1DB; 6 GO
6.启用会话并发动请求。复制以下代码并将其粘贴到查询编辑器窗口中。 然后,运行该代码以启动会话并向 //AWDB/1DBSample/TargetService 发送请求消息。 该代码必须在一个块中运行,因为是使用变量将对话句柄从 BEGIN DIALOG 语句传递到 SEND 语句。 批处理运行 BEGIN DIALOG 语句,以启动会话。 它会生成一个请求消息,然后使用 SEND 语句中的对话句柄发送该会话的请求消息。 最后一条 SELECT 语句显示已发送消息的文本。
1 DECLARE @InitDlgHandle UNIQUEIDENTIFIER; 2 DECLARE @RequestMsg NVARCHAR(100); 3 4 BEGIN TRANSACTION; 5 6 BEGIN DIALOG @InitDlgHandle 7 FROM SERVICE 8 [//AWDB/1DBSample/InitiatorService] 9 TO SERVICE 10 N'//AWDB/1DBSample/TargetService' 11 ON CONTRACT 12 [//AWDB/1DBSample/SampleContract] 13 WITH 14 ENCRYPTION = OFF; 15 16 SELECT @RequestMsg = 17 N'<RequestMsg>Message for Target service.</RequestMsg>'; 18 19 SEND ON CONVERSATION @InitDlgHandle 20 MESSAGE TYPE 21 [//AWDB/1DBSample/RequestMessage] 22 (@RequestMsg); 23 24 SELECT @RequestMsg AS SentRequestMsg; 25 26 COMMIT TRANSACTION; 27 GO
7. 接收请求并发送答复。复制以下代码并将其粘贴到查询编辑器窗口中。 然后,运行该代码以接收来自 TargetQueue1DB 的答复消息,并将答复消息发回给发起方。 RECEIVE 语句可检索该请求消息。 以下 SELECT 语句将显示文本,以便您可以验证它是否与上一步中发送的消息相同。 IF 语句测试所接收的消息是否是请求消息类型,并测试是否使用了 SEND 语句将答复消息发送回发起方。 END CONVERSATION 语句用于结束会话的目标端。 最后一条 SELECT 语句显示答复消息的文本。
1 DECLARE @RecvReqDlgHandle UNIQUEIDENTIFIER; 2 DECLARE @RecvReqMsg NVARCHAR(100); 3 DECLARE @RecvReqMsgName sysname; 4 5 BEGIN TRANSACTION; 6 7 WAITFOR 8 ( RECEIVE TOP(1) 9 @RecvReqDlgHandle = conversation_handle, 10 @RecvReqMsg = message_body, 11 @RecvReqMsgName = message_type_name 12 FROM TargetQueue1DB 13 ), TIMEOUT 1000; 14 15 SELECT @RecvReqMsg AS ReceivedRequestMsg; 16 17 IF @RecvReqMsgName = 18 N'//AWDB/1DBSample/RequestMessage' 19 BEGIN 20 DECLARE @ReplyMsg NVARCHAR(100); 21 SELECT @ReplyMsg = 22 N'<ReplyMsg>Message for Initiator service.</ReplyMsg>'; 23 24 SEND ON CONVERSATION @RecvReqDlgHandle 25 MESSAGE TYPE 26 [//AWDB/1DBSample/ReplyMessage] 27 (@ReplyMsg); 28 29 END CONVERSATION @RecvReqDlgHandle; 30 END 31 32 SELECT @ReplyMsg AS SentReplyMsg; 33 34 COMMIT TRANSACTION; 35 GO
8.接收答复并结束会话。
1 DECLARE @RecvReplyMsg NVARCHAR(100); 2 DECLARE @RecvReplyDlgHandle UNIQUEIDENTIFIER; 3 4 BEGIN TRANSACTION; 5 6 WAITFOR 7 ( RECEIVE TOP(1) 8 @RecvReplyDlgHandle = conversation_handle, 9 @RecvReplyMsg = message_body 10 FROM InitiatorQueue1DB 11 ), TIMEOUT 1000; 12 13 END CONVERSATION @RecvReplyDlgHandle; 14 15 SELECT @RecvReplyMsg AS ReceivedReplyMsg; 16 17 COMMIT TRANSACTION; 18 GO
9.删除会话对象。复制以下代码并将其粘贴到查询编辑器窗口中。 然后,运行代码以删除用于支持该会话的对象
1 IF EXISTS (SELECT * FROM sys.services 2 WHERE name = 3 N'//AWDB/1DBSample/TargetService') 4 DROP SERVICE 5 [//AWDB/1DBSample/TargetService]; 6 7 IF EXISTS (SELECT * FROM sys.service_queues 8 WHERE name = N'TargetQueue1DB') 9 DROP QUEUE TargetQueue1DB; 10 11 -- Drop the initiator queue and service if they already exist. 12 IF EXISTS (SELECT * FROM sys.services 13 WHERE name = 14 N'//AWDB/1DBSample/InitiatorService') 15 DROP SERVICE 16 [//AWDB/1DBSample/InitiatorService]; 17 18 IF EXISTS (SELECT * FROM sys.service_queues 19 WHERE name = N'InitiatorQueue1DB') 20 DROP QUEUE InitiatorQueue1DB; 21 22 IF EXISTS (SELECT * FROM sys.service_contracts 23 WHERE name = 24 N'//AWDB/1DBSample/SampleContract') 25 DROP CONTRACT 26 [//AWDB/1DBSample/SampleContract]; 27 28 IF EXISTS (SELECT * FROM sys.service_message_types 29 WHERE name = 30 N'//AWDB/1DBSample/RequestMessage') 31 DROP MESSAGE TYPE 32 [//AWDB/1DBSample/RequestMessage]; 33 34 IF EXISTS (SELECT * FROM sys.service_message_types 35 WHERE name = 36 N'//AWDB/1DBSample/ReplyMessage') 37 DROP MESSAGE TYPE 38 [//AWDB/1DBSample/ReplyMessage]; 39 GO