无法关闭Service Broker对话

时间:2014-09-20 13:08:54

标签: 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. 发起人向Target发送消息。
  2. 目标队列通知事件触发。
  3. 外部激活器服务执行以响应目标队列通知事件;发出RECEIVE TOP(1)以检索发送给Target的消息。
  4. 外部激活器服务完成处理并执行END CONVERSATION @ConversationHandle。
  5. 第4步的END CONVERSATION会向发起人发出一条密切的消息; Initiator的内部激活服务运行以响应Initiator队列中消息的出现,进行处理并发出END CONVERSATION @ConversationHandle。
  6. 不幸的是,谈话的发起人一方关闭但目标方从未这样做过;它处于DISCONNECTED_INBOUND状态。

    当External Activator服务发出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);
    

1 个答案:

答案 0 :(得分:2)

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

更新

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

但是您注意到处于DISCONNECTED_INBOUND状态的会话。这是一个收到EndDialog消息的会话,但应用程序没有发出END CONVERSION。

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