我在关闭Service Broker对话时遇到问题,我想从概念上理解事情应该如何运作。 请注意,我的代码基于Dan Guzman在http://www.dbdelta.com/service-broker-external-activator-example/的示例。
鉴于以下SQL Server 2012 Service Broker配置:
这是我对流程的理解:
不幸的是,谈话的发起人一方关闭但目标方从未这样做过;它处于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);
答案 0 :(得分:2)
这里涉及两个对话。一个是您自己的应用对话,一个是通知EA的系统对话。根据您描述代码的方式,您依靠EA来关闭对话,但这只会关闭系统对话。您还需要关闭应用对话,即从目标队列接收的句柄。
更新
您的已激活服务应在收到的@handle上发出RECEIVE ... FROM Target
然后END CONVERSATION
。你的解释说这就是你所做的(步骤4),但如果你这样做,那么目标对话将在30分钟内清理干净。
但是您注意到处于DISCONNECTED_INBOUND状态的会话。这是一个收到EndDialog消息的会话,但应用程序没有发出END CONVERSION。
因此,您解释应用程序正在执行的操作与观察到的对话状态之间存在脱节。我想代码中可能存在错误,而代码并没有完全按照您的想法执行。尝试在此处发布代码的相关部分。