service broker是sql server唯一原生、事务安全、持久化的异步消息队列机制;需显式启用数据库级ssb,创建消息类型、契约、队列、服务,通过begin dialog和send发送序列化参数,由激活存储过程自动接收处理,并严格管理会话生命周期。

Service Broker 是 SQL Server 唯一原生支持异步消息队列的机制
SQL Server 本身不支持“在存储过程中启动后台线程”或类似 async/await 的语法。所谓“异步执行逻辑”,本质是把耗时操作从当前事务中剥离出去,靠消息驱动后续处理。Service Broker(SSB)是唯一被官方支持、事务安全、可持久化、能跨数据库甚至跨实例通信的方案——别试图用 WAITFOR DELAY 或轮询临时表模拟异步,那只是阻塞伪装。
必须显式启用 Service Broker 并创建完整消息栈
常见错误是只建了队列却忘了启用数据库级 SSB,或者消息类型/契约没配对,导致 SEND 报错 Service Broker is not enabled 或 The conversation endpoint is in an error state。
- 启用 SSB:
ALTER DATABASE [YourDB] SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE(注意加WITH ROLLBACK IMMEDIATE避免被连接阻塞) - 最小必要对象:消息类型(
CREATE MESSAGE TYPE)、契约(CREATE CONTRACT)、队列(CREATE QUEUE)、服务(CREATE SERVICE) - 发送端和服务端可用同一队列,但推荐分离:一个入队列(接收请求),一个出队列(处理结果),避免死锁和读写竞争
在存储过程中用 BEGIN DIALOG + SEND 触发异步流程
关键不是“怎么让 SP 不等”,而是“怎么把参数可靠塞进消息体”。SSB 消息体是 VARBINARY(MAX),不能直接传表变量或复杂结构,需序列化。
- 简单场景:用
FOR JSON AUTO或SELECT ... FOR XML将参数转为字符串再CAST(... AS VARBINARY(MAX)) - 务必用
BEGIN DIALOG创建会话句柄,再SEND ON CONVERSATION,不能跳过对话直接发 - 示例片段:
DECLARE @h UNIQUEIDENTIFIER; BEGIN DIALOG @h FROM SERVICE [//MyApp/Initiator] TO SERVICE '//MyApp/Target' ON CONTRACT [//MyApp/ProcessingContract]; SEND ON CONVERSATION @h MESSAGE TYPE [//MyApp/RequestMsg] (CAST((SELECT @OrderID, @Priority FOR JSON PATH) AS VARBINARY(MAX)));
- 发送后立即
COMMIT,否则会话可能回滚;不要在同一个事务里又去RECEIVE,那是同步反模式
用激活存储过程(ACTIVATION PROCEDURE)自动消费消息
没人手动跑 RECEIVE,SSB 的“异步”才成立。激活过程由 SQL Server 自动调用,但极易因异常退出导致队列停摆。
- 必须用
CREATE PROCEDURE ... WITH EXECUTE AS OWNER,避免权限不足中断激活 - 激活过程开头加
BEGIN TRY ... END TRY BEGIN CATCH ... END CATCH,捕获所有错误并END CONVERSATION,否则未结束的会话堆积,队列状态变成UNINITIALIZED - 每次
RECEIVE最多取一条(TOP(1)),处理完再循环,别用WHILE @@ROWCOUNT > 0无界循环——超时或阻塞会导致激活挂起 - 消息体反序列化示例:
SELECT @json = CAST(message_body AS NVARCHAR(MAX)) FROM @messages; SELECT @OrderID = JSON_VALUE(@json, '$.OrderID');
真正难的不是发消息,而是会话生命周期管理:对话未正确 END CONVERSATION 会卡住队列,而过早结束又丢消息。生产环境务必监控 sys.transmission_queue 和 sys.conversation_endpoints,别等积压几万条才发现激活过程静默失败了。










