(有关解决方案,请参阅我在此原始帖子底部的编辑)
设置
我的 Microsoft SQL Server 2005 Express 数据库中有两个存储过程:
WaitForMyMessage(@myName NVARCHAR(50), @myMessage NVARCHAR(MAX) OUTPUT)
ProvideMessage(@name NVARCHAR(50), @message NVARCHAR(MAX))
我想WaitForMyMessage()
阻止,直到有人ProvideMessage()
用相应的名字打电话。如果有人已经ProvideMessage()
使用该名称呼叫过,那么WaitForMyMessage()
将立即返回提供的值。
我最初考虑使用具有 FIFO 队列行为的简单表来实现这一点,但找不到阻止INSERT
进入该表的方法。因此WaitForMyMessage()
必须进行民意调查,出于显而易见的原因,这是不可接受的。
Q1:
有没有一种有效的方法来阻止直到某个记录出现在表中?该WAITFOR
语句会很棒,但 SQL 似乎不支持它进行查询(仅支持DELAY
、TIME
或RECEIVE
)。但类似的东西会很棒,
例如:
-- It would be great is SQL supported this, but as far as I can tell it doesn't.
DECLARE @t TABLE (ans NVARCHAR(MAX));
WAITFOR (
WITH A AS (
SELECT TOP (1) *
FROM ProviderMessage A
WHERE ProviderMessage.Name = @myName
ORDER BY A.ID
)
DELETE FROM A
OUTPUT deleted.ID INTO @t
);
SET @myMessage = (SELECT ans FROM @t);
所以它会一直闲置,直到有人将具有适当的记录插入Name
表ProviderMessage
中,一旦发生这种情况,该记录将被上面删除,同时检索其Value
字段以返回给调用者。
其他想法
唉,我找不到Q1的答案,所以我继续使用 Service Broker 提供的实际消息队列来实现这个。考虑到 Service Broker 的力量和影响力,这似乎有点过头了,但是如果没有Q1的答案,我不得不试一试。我将我的服务和简单队列定义如下:
CREATE QUEUE q1
CREATE SERVICE s1 ON QUEUE q1 ([DEFAULT])
然后WaitForMyMessage()
变成了:
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (
SELECT FarHandle
FROM ProviderInfo
WHERE ProviderInfo.Name = @myName
);
WAITFOR (
RECEIVE @myMessage = CONVERT(NVARCHAR(MAX), message_body)
FROM q1
WHERE conversation_handle = @farHandle
);
并ProvideMessage()
会发送消息,如下所示:
DECLARE @nearHandle UNIQUEIDENTIFIER;
SET @nearHandle = (
SELECT NearHandle
FROM ProviderInfo
WHERE ProviderInfo.Name = @name
);
SEND ON CONVERSATION @nearHandle (@message)
这一切都运行良好,除了一件事:似乎 Service Broker 不支持发现给定对话的近端和远端句柄。我必须两者都知道,这样我才能为两个过程填充ProviderInfo
表格以进行私下通信。
Q2:
如何才能同时获得新对话的近端对话句柄和远端对话句柄? 现在我通过这样的查询来做到这sys.conversation_endpoints
一点:
-- Create the conversation
DECLARE @nearHandle UNIQUEIDENTIFIER;
BEGIN DIALOG CONVERSATION @nearHandle
FROM SERVICE s1
TO SERVICE 's1'
WITH ENCRYPTION = OFF;
-- Queue an initialization message
SEND ON CONVERSATION @nearHandle ('');
-- Figure out the handle to the receiving side of this conversation
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (
SELECT conversation_handle
FROM sys.conversation_endpoints
WHERE conversation_id = (
SELECT A.conversation_id
FROM sys.conversation_endpoints A
WHERE A.conversation_handle = @nearHandle
) AND conversation_handle <> @nearHandle
);
-- Get our initialization message out of the queue
DECLARE @unused TINYINT;
WAITFOR (
RECEIVE @unused = status
FROM q1
WHERE conversation_handle = @farHandle
);
-- Store both conversation handles, associated with this name
INSERT INTO ProviderInfo (Name, NearHandle, FarHandle)
SELECT @name, @nearHandle, @farHandle
但是由于 Service Broker 架构旨在支持更复杂的场景,包括分布式服务等,消息sys.transmission_queue
甚至可能被放置在本地,以及其他实现的复杂性,我对我的方法对于生产来说足够健壮不是很有信心.
因此,如果我这样做的方式不健全,是否有“正确”的方式?我考虑尝试通过使用对话组来避免这种需要,但由于基本相同的问题而无法解决(对话组 ID 也没有传达给远端),并且我发现这些主题没有提供解决方案任何一个:
结论
实现这项工作的障碍让我担心它不应该以这种方式使用,因此要么在某些生产场景中不起作用,要么将来可能不受支持。任何人都可以提供表明这种方式可靠的文档,或提供可靠且仍然有效的替代解决方案(有或没有 Service Broker)吗?
谢谢!
编辑:(解决方案)
这里的中心问题是 Q2(如何同时获得新对话的近端对话句柄和远端对话句柄?)。
多亏了几位贡献者的好主意,答案浮出水面(现在看起来很明显!)是通过在初始化消息SELECT
之后立即从队列本身中获取远程句柄,因为该语句允许您过滤任何排队列!SEND
SELECT
因此,不要像我原来的帖子那样这样做:
-- Queue an initialization message
SEND ON CONVERSATION @nearHandle ('');
-- Figure out the handle to the receiving side of this conversation
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (
SELECT conversation_handle
FROM sys.conversation_endpoints
WHERE conversation_id = (
SELECT A.conversation_id
FROM sys.conversation_endpoints A
WHERE A.conversation_handle = @nearHandle
) AND conversation_handle <> @nearHandle
);
-- Get our initialization message out of the queue
...
我们可以更简单(更有效!)这样做:
-- Queue an initialization message with a unique identifier
DECLARE @initbin VARBINARY(36);
SET @initbin = CONVERT(VARBINARY(32), @nearHandle);
SEND ON CONVERSATION @nearHandle (@initbin);
-- Figure out the handle to the receiving side of this conversation using the known unique identifier
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (SELECT conversation_handle FROM q1 WHERE message_body = @initbin)
-- Get our initialization message out of the queue
...
谢谢大家!