【问题标题】:Async procedure call using service broker in Sql Server在 Sql Server 中使用服务代理进行异步过程调用
【发布时间】:2018-02-21 18:52:51
【问题描述】:

我有一个每天按计划运行的备份过程(生产),并且生成的备份会定期用于刷新较低的环境。我正在构建一个自动化解决方案来将 .bak 文件下载到较低的环境(不同的服务器)。我不愿意在备份中使用 MIRROR TO 选项,因为如果镜像到 .我在这里想使用服务代理并在备份过程结束时异步触发从 prod 到较低环境的文件复制。我在理解服务代理、消息类型、服务、队列和合同方面付出了很多努力。一切都很好,直到这里。现在我只想了解如何使用服务代理在备份结束时使用一些自定义逻辑触发文件复制过程。我已经使用以下脚本来了解服务代理。有人请指导我如何从中构建解决方案。

------------------------------------------------------------SETUP--------------------------------------------

CREATE MESSAGE TYPE [//SBTest/SBSample/RequestMessage] VALIDATION=WELL_FORMED_XML;

CREATE MESSAGE TYPE [//SBTest/SBSample/ReplyMessage] VALIDATION=WELL_FORMED_XML; 
-------------------------------------------------------------------------------------------------------------
CREATE CONTRACT [//SBTest/SBSample/SBContract]
(
[//SBTest/SBSample/RequestMessage] SENT BY INITIATOR ,
[//SBTest/SBSample/ReplyMessage] SENT BY TARGET 
);
-------------------------------------------------------------------------------------------------------------
CREATE QUEUE SBInitiatorQueue; 

CREATE QUEUE SBTargetQueue; 
-------------------------------------------------------------------------------------------------------------
CREATE SERVICE [//SBTest/SBSample/SBInitiatorService] ON QUEUE SBInitiatorQueue; 

CREATE SERVICE [//SBTest/SBSample/SBTargetService] ON QUEUE SBTargetQueue ([//SBTest/SBSample/SBContract]); 

-------------------------------------------------------------------------------------------------------------





-------------------------------------------------------INITIATE QUE-----------------------------------------
DECLARE @InitDlgHandle UNIQUEIDENTIFIER
DECLARE @RequestMessage VARCHAR(1000) 

BEGIN TRAN 

--Determine the Initiator Service, Target Service and the Contract 

BEGIN DIALOG @InitDlgHandle
FROM SERVICE [//SBTest/SBSample/SBInitiatorService] TO SERVICE'//SBTest/SBSample/SBTargetService'
ON CONTRACT
[//SBTest/SBSample/SBContract]
WITH ENCRYPTION=OFF; 


--Prepare the Message
SELECT @RequestMessage = N'HAHA'; 


--Send the Message
SEND ON CONVERSATION @InitDlgHandle 
MESSAGE TYPE
[//SBTest/SBSample/RequestMessage]
(@RequestMessage);

SELECT @RequestMessage AS SentRequestMessage;

COMMIT TRAN 
---------------------------------------------------------------------------------------------------------------






-------------------------------------------------------READ QUE-------------------------------------------------

\DECLARE @TargetDlgHandle UNIQUEIDENTIFIER
DECLARE @ReplyMessage VARCHAR(1000)
DECLARE @ReplyMessageName Sysname 

BEGIN TRAN; 

--Receive message from Initiator
RECEIVE TOP(1)
@TargetDlgHandle=Conversation_Handle, @ReplyMessage=Message_Body, @ReplyMessageName=Message_Type_Name
 FROM SBTargetQueue; 

SELECT @ReplyMessage AS ReceivedRequestMessage; 

-- Confirm and Send a reply
IF @ReplyMessageName=N'HAHA'

BEGIN
DECLARE @RplyMsg VARCHAR(1000)

SELECT @RplyMsg =N'HI'; 

SEND ON CONVERSATION @TargetDlgHandle
MESSAGE TYPE
[//SBTest/SBSample/ReplyMessage]
(@RplyMsg);
END CONVERSATION @TargetDlgHandle;

END 

SELECT @RplyMsg AS SentReplyMessage; 

COMMIT TRAN;

-------------------------------------------------------------------------------------------------------------------

【问题讨论】:

    标签: sql sql-server message-queue service-broker loose-coupling


    【解决方案1】:

    使用“内部激活”进行触发。您需要创建一个存储过程,当消息进入队列时将触发该存储过程,从队列中读取,然后执行工作。 改编自link

    CREATE PROCEDURE TargetActivProc
    AS
      DECLARE @RecvReqDlgHandle UNIQUEIDENTIFIER;
      DECLARE @RecvReqMsg NVARCHAR(100);
      DECLARE @RecvReqMsgName sysname;
    
      WHILE (1=1)
      BEGIN
    
        BEGIN TRANSACTION;
    
        WAITFOR
        ( RECEIVE TOP(1)
            @RecvReqDlgHandle = conversation_handle,
            @RecvReqMsg = message_body,
            @RecvReqMsgName = message_type_name
          FROM TargetQueueIntAct
        ), TIMEOUT 5000;
    
        IF (@@ROWCOUNT = 0)
        BEGIN
          ROLLBACK TRANSACTION;
          BREAK;
        END
    
        IF @RecvReqMsgName =
           N'//SBTest/SBSample/RequestMessage'
        BEGIN
    
          --do work here
        END
        ELSE IF @RecvReqMsgName =
            N'http://schemas.microsoft.com/SQL/ServiceBroker/EndDialog'
        BEGIN
           END CONVERSATION @RecvReqDlgHandle;
        END
        ELSE IF @RecvReqMsgName =
            N'http://schemas.microsoft.com/SQL/ServiceBroker/Error'
        BEGIN
           END CONVERSATION @RecvReqDlgHandle;
        END
    
        COMMIT TRANSACTION;
    
      END
    GO
    

    下一部分是在队列上启用激活

    ALTER QUEUE SBTargetQueue
        WITH ACTIVATION
        ( STATUS = ON,
          PROCEDURE_NAME = TargetActivProc,
          MAX_QUEUE_READERS = 1,
          EXECUTE AS SELF
        );
    

    当消息到达队列时,该过程将被激活,并将保持激活并循环,直到队列为空。

    【讨论】:

    • Richard Cupples 有一段时间如果队列空了,是什么再次触发它?队列中的新消息触发它?
    • @TharunRaja 没错。只要队列中有消息,SQL Server 就会定期检查激活的进程数是否小于 MAX_QUEUE_READERS,然后启动一个新进程。当这些进程中的每一个从队列中返回零行时,它们都将终止。
    • 我已经尝试过更改队列脚本并附加了一些随机过程名称,它确实插入到一个虚拟表中。有时,当队列中有消息时,我看到附加到队列的过程被执行,有时我看不到它被执行。 MAX_QUEUE_READERS 选项和队列中的消息数是什么?您的回答对我了解服务经纪人有很大帮助。谢谢。
    • MAX_QUEUE_READERS 是 SQL Server 将同时激活的读取器进程的最大数量,由每个队列定义。
    • 正如我所问的,有时附加到 target_queue 的过程会被触发,有时甚至在队列有新消息传入时也不会触发。可能是什么原因?也可以将两个不同的过程附加到队列中。只是好奇与服务代理同时触发两个不同的程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 2019-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多