sql-server-2012 - 无法关闭 Service Broker 对话

标签 sql-server-2012 service-broker

我在关闭 Service Broker 对话时遇到问题,我想从概念上了解事情应该如何工作。 请注意,我的代码基于 Dan Guzman 在 http://www.dbdelta.com/service-broker-external-activator-example/ 的示例。 .

假设有以下 SQL Server 2012 Service Broker 配置:

  • 发起人
  • 启动器服务 - 内部激活
  • 目标
  • 目标队列监视器
  • 外部激活器服务(与 REST 服务通信的 SSIS 应用)

这是我对流程的理解:

  1. 发起方向目标方发送消息。
  2. 触发目标队列通知事件。
  3. 外部激活器服务执行以响应目标队列通知事件;发出 RECEIVE TOP (1) 以检索发送到 Target 的消息。
  4. 外部激活器服务完成处理并执行 END CONVERSATION @ConversationHandle。
  5. 第 4 步的 END CONVERSATION 会向发起者发送一条结束消息;启动器的内部激活服务运行以响应启动器队列中消息的出现,进行处理并发出结束对话@ConversationHandle。

不幸的是, session 的发起方关闭了,但目标方却从未关闭;它处于 DISCONNECTED_INBOUND 状态。

当外部激活器服务发出 END CONVERSATION @ConversationHandle 时,对话的目标端不会关闭吗?如果没有,如何关闭目标方的对话?

        -- Enabling service broker
        USE master
        ALTER DATABASE my_database
        SET ENABLE_BROKER; 

        --Create Message Types for Request and Response messages

        -- For Request
        CREATE MESSAGE TYPE
        [ABCEventRequestMessage]
        VALIDATION=WELL_FORMED_XML; 

        -- For Response
        CREATE MESSAGE TYPE
        [ABCEventResponseMessage]
        VALIDATION=WELL_FORMED_XML; 

        --Create Contract for the Conversation 

        CREATE CONTRACT [ABCEventContract]
        (
            [ABCEventRequestMessage] SENT BY INITIATOR 
            -- must make the reply message 'SENT BY ANY'
            -- so the External Activator service can
            -- send it to the Initiator after talking
            -- to the webservice
            ,[ABCEventResponseMessage] SENT BY ANY
        );

        --Create Queue for the Initiator
        CREATE QUEUE ABCEventInitiatorQueue
        WITH STATUS = ON,
        ACTIVATION (
          PROCEDURE_NAME = dbo.pci_LogABCEventTransferResult,
          MAX_QUEUE_READERS = 1,
          EXECUTE AS SELF
        ); 

        --Create Queue for the Target
        CREATE QUEUE ABCEventTargetQueue; 

        --Create Queue for the External Activator
       CREATE QUEUE ExternalActivatorQueue;

        --Create Service for the Target and the Initiator.

        --Create Service for the Initiator.
        -- NOTE: not specifying a contract on the queue
        -- means that the service can initiate conversations
        -- but it can't be a target for any other services
        CREATE SERVICE [ABCEventInitiatorService]
        ON QUEUE ABCEventInitiatorQueue;

        --Create Service for the Target.
        CREATE SERVICE [ABCEventTargetService] 
        ON QUEUE ABCEventTargetQueue
        ([ABCEventContract]); 

        --Create Service for the External Activator
        CREATE SERVICE ExternalActivatorService
        ON QUEUE ExternalActivatorQueue
        (
          [http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]
        );

      CREATE EVENT NOTIFICATION EventNotificationABCEventTargetQueue
          ON QUEUE ABCEventTargetQueue
          FOR QUEUE_ACTIVATION
          TO SERVICE 'ExternalActivatorService', 'current database';
    });

        IF OBJECT_ID('dbo.pci_InitiateABCEventTransfer', 'P') IS NULL
        BEGIN
          EXEC ('CREATE PROCEDURE dbo.pci_InitiateABCEventTransfer as SELECT 1')
        END;

        ALTER PROC dbo.pci_InitiateABCEventTransfer

        @CompleteTriggerXMLOut XML = NULL OUTPUT
        ,@ConversationHandle uniqueidentifier = NULL OUTPUT
        ,@CompleteTriggerXMLIn XML

        ---------------------------------------------
        --called by application to trigger batch process
        --Sample Usage:
        --
        -- EXEC dbo.pci_InitiateABCEventTransfer @@CompleteTriggerXML = 'your_xml_here';
        -- NOTE: when calling this stored procedure from a SQL Server Mgmt
        -- Studio query window, enclose the xml data in single quotes;
        -- if you use double quotes instead, SQL Server thinks
        -- you are specifying a field name in which to save the data
        -- and returns an error like 'Maximum length is 128'
        ---------------------------------------------
        AS
        DECLARE
        --@conv_hand uniqueidentifier
        @message_body varbinary(MAX);

        SET @CompleteTriggerXMLOut = @CompleteTriggerXMLin

        BEGIN TRY

        BEGIN TRAN;

        BEGIN DIALOG CONVERSATION @ConversationHandle
        FROM SERVICE ABCEventInitiatorService
        TO SERVICE 'ABCEventTargetService', 'CURRENT DATABASE'
        ON CONTRACT ABCEventContract
        WITH
        ENCRYPTION = OFF,
        LIFETIME = 6000;

        -- NOTE: because we created our services with a specific
        -- contract, ABCEventContract, that includes specific
        -- message types (ABCEventRequestMessage & ABCEventResponseMessage)
        -- but does not include the DEFAULT message type,
        -- we must include the MESSAGE TYPE clause
        -- of the SEND ON CONVERSATION command; without this clause,
        -- Service Broker will assume the DEFAULT message type
        -- and the SEND will fail, saying, "The message TYPE 'DEFAULT'
        -- is not part of the service contract."

        SEND ON CONVERSATION @ConversationHandle
        MESSAGE TYPE ABCEventRequestMessage
        (@CompleteTriggerXMLOut);

        COMMIT;
        END TRY
        BEGIN CATCH
        THROW;
        END CATCH;

        SELECT @CompleteTriggerXMLOut, @ConversationHandle;

        PRINT 'CompleteTriggerXMLOut = ' + CONVERT(nvarchar(max), @CompleteTriggerXMLOut);

        RETURN @@ERROR; 

        IF OBJECT_ID('dbo.pci_GetABCEventDetails', 'P') IS NULL
        BEGIN
          EXEC ('CREATE PROCEDURE dbo.pci_GetABCEventDetails as SELECT 1')
        END;

        ALTER PROC dbo.pci_GetABCEventDetails
        --------------------------------------
        --called by SSIS package at start ---
        --------------------------------------
        AS
        DECLARE
        @conversation_handle uniqueidentifier
        ,@message_type_name sysname
        ,@message_body xml
        ,@parameter1 int;

        BEGIN TRY

        BEGIN TRAN;

        RECEIVE TOP(1)
        @conversation_handle = conversation_handle
        ,@message_type_name = message_type_name
        ,@message_body = message_body
        FROM dbo.ABCEventTargetQueue;

        IF @@ROWCOUNT = 0
        BEGIN
        RAISERROR ('No messages received from dbo.ABCEventTargetQueue', 16, 1);
        RETURN 1;
        END;

        INSERT INTO dbo.ABCEventTransferLog(
        ConversationHandle
        ,MessageTypeName
        ,MessageBody
        )
        VALUES(
        @conversation_handle
        ,@message_type_name
        ,CAST(@message_body AS varbinary(MAX))
        );

        COMMIT;

        SELECT
            CAST(@message_body AS nvarchar(MAX)) AS WhatIsThis
            ,@conversation_handle AS ConversationHandle
            ,@parameter1 AS Parameter1;

        END TRY
        BEGIN CATCH
        THROW;
        END CATCH;

        RETURN @@ERROR;

        IF OBJECT_ID('dbo.pci_CompleteABCEventTransfer', 'P') IS NULL
        BEGIN
          EXEC ('CREATE PROCEDURE dbo.pci_CompleteABCEventTransfer as SELECT 1')
        END;

            ALTER PROC dbo.pci_CompleteABCEventTransfer
          @ConversationHandle uniqueidentifier
                ,@WebserviceResponseStatusCode integer
                ,@WebserviceResponseXML xml
           ------------------------------------------
           -- called by SSIS package at completion
           -- Sample Usage:

           -- normal completion:
           -- EXEC dbo.pci_CompleteABCEventTransfer
           -- @ConversationHandle = '00000000-0000-0000-0000-000000000000';

           -- completed with error:
           -- EXEC dbo.pci_CompleteABCEventTransfer
           -- @ConversationHandle = '00000000-0000-0000-0000-000000000000'
           -- @ErrorMessage = 'an error occurred';
           ------------------------------------------
         AS

         IF @WebserviceResponseStatusCode <> 201
               -- webservice record creation failed;
               -- complete conversation with error
         BEGIN
           END CONVERSATION @ConversationHandle
           WITH ERROR = 1
           DESCRIPTION = 'Something went horribly wrong';
         END;
               -- webservice created record in remote system;
               -- complete conversation normally
               ELSE
         BEGIN
           END CONVERSATION @ConversationHandle;
         END

         RETURN @@ERROR;

  # because of circular references, must create a "dummy"
  # LogABCEventTransferResult stored procedure first,
  # create the ABCEventInitiatorQueue and then replace
  # the dummy stored procedure with the real one

      IF OBJECT_ID('dbo.pci_LogABCEventTransferResult', 'P') IS NULL
      BEGIN
        EXEC ('CREATE PROCEDURE dbo.pci_LogABCEventTransferResult as SELECT 1')
      END;

      ALTER PROC dbo.pci_LogABCEventTransferResult
      ---------------------------------------------
      --initiator queue activated proc to process messages
      ---------------------------------------------
      AS
      DECLARE
      @conversation_handle uniqueidentifier
      ,@message_type_name sysname
      ,@message_body varbinary(MAX);
      WHILE 1 = 1
      BEGIN
      WAITFOR (
        RECEIVE TOP (1)
        @conversation_handle = conversation_handle
        ,@message_type_name = message_type_name
        ,@message_body = message_body
        FROM dbo.ABCEventInitiatorQueue
      ), TIMEOUT 1000;
      IF @@ROWCOUNT = 0
      BEGIN
      --exit when no more messages
      RETURN;
      END;

      --log message
      INSERT INTO dbo.ABCEventTransferLog(
        ConversationHandle
        ,MessageTypeName
        ,MessageBody
      )
      VALUES(
        @conversation_handle
        ,@message_type_name
        ,@message_body
      );
      END CONVERSATION @conversation_handle;
      END;

      --log table
      CREATE TABLE dbo.ABCEventTransferLog(
      ConversationHandle uniqueidentifier NOT NULL
      ,MessageTypeName sysname NOT NULL
      ,MessageBody varbinary(MAX) NULL
      ,LogTime datetime2(3) NOT NULL
      CONSTRAINT DF_ServiceBrokerLog_LogTime
      DEFAULT (SYSDATETIME())
      );
      CREATE CLUSTERED INDEX cdx_ABCEventTransferLog ON dbo.ABCEventTransferLog(LogTime);

最佳答案

这里涉及两个对话。一种是您自己的应用程序对话,一种是通知 EA 的系统对话。从你如何描述你的代码来看,你依靠EA来关闭对话,但这只会关闭系统对话。您还需要关闭应用程序对话,即从目标队列接收的句柄。

更新:

您激活的服务应该在收到的@handle上发出RECEIVE ... FROM Target,然后发出END CONVERSATION。您的解释表明这就是您所做的(第 4 步),但如果您这样做,那么目标对话将在 30 分钟内正确清理。

但是您注意到对话处于 DISCONNECTED_INBOUND 状态。这是一个已收到 EndDialog 消息但应用程序未对其发出 END CONVERSION 的对话。

因此,您所解释的应用程序正在执行的操作与观察到的对话状态之间存在脱节。我猜代码中可能存在错误,并且代码并不完全按照您的想法执行。尝试在此处发布代码的相关部分。

关于sql-server-2012 - 无法关闭 Service Broker 对话,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25949132/

相关文章:

带连接的 SQL Server 更新语句

SSIS包卡在 "Created Execution"状态

sql - 在 SQL 中使用 XML 时的 Where 子句

sql - 创建具有泛型类型的函数

sql-server - 无法复制从 SQL Server 中创建的安全证书文件

sql - 有没有办法从分区表中删除范围值(sql server 2012)

sql-server - 激活脚本挂起,无法关闭

wcf - MSMQ 与 SQL Server 服务代理

sql-server - 如何快速关闭 Microsoft SQL Server Service Broker 的所有元素?

sql-server - 从 SQL Server Service Broker 调用带有 SSISDB 实现的 SSIS