了解发生激活的条件

Service Broker 激活过程由两个步骤组成。第一步,Service Broker 确定激活是否是必需的。第二步,Service Broker 确定是否发生激活。尽管内部激活和外部激活的确切过程有所不同,但这两种策略涉及的总体概念是相同的。

确定激活是否是必需的

只要新队列读取器有要执行的有用工作,就必须进行激活。由队列监视器确定激活是否是必需的。Service Broker 为具有激活状态 STATUS = ON 或已为其注册 QUEUE_ACTIVATION 事件通知的每个队列都创建队列监视器。动态管理视图sys.dm_broker_queue_monitors (Transact-SQL)中列出了在实例中处于活动状态的队列监视器。每个队列监视器都会跟踪以下内容:

队列是否包含准备接收的消息。

针对队列的 RECEIVE 语句最近一次返回空结果集是在什么时候。

相应队列当前有多少激活存储过程正在运行。

队列监视器每隔几秒并且在以下事件中的一个或多个发生时检查激活是否是必需的:

  • 新消息到达队列中。

  • SQL Server 为队列执行 RECEIVE 语句。

  • 包含 RECEIVE 语句的事务回滚。

  • 由队列监视器启动的所有存储过程退出。

  • SQL Server 为队列执行 ALTER 语句。

当满足以下情况之一时,激活是必需的:

  • 新消息到达不包含任何未读消息的队列,并且未为此队列运行激活存储过程。

  • 队列包含未读消息,在 GET CONVERSATION GROUP 语句或不具有 WHERE 子句的 RECEIVE 语句中没有任何会话在等待,并且在几秒钟内没有任何 GET CONVERSATION GROUP 语句或不具有 WHERE 子句的 RECEIVE 语句返回空结果集。换言之,就是在消息由于激活的存储过程无法足够快地读取它们而在队列中蓄积时。

实际上,利用此过程,队列监视器可以判断处理队列的队列读取器的数量是否能跟上传入消息的通信流量。请注意,此方法会考虑使用会话组锁定。由于一次只有一个队列读取器可以为会话处理消息,因此如果因响应更简单的方法(例如,队列中未读消息的数量)而启动队列读取器,则可能会浪费资源。Service Broker 激活会考虑新队列读取器是否将有有用的工作可做。

例如,队列可能包含单个会话中的大量未处理消息。在此情况下,只有一个队列读取器可以处理这些消息。队列监视器激活另一个队列读取器。由于所有这些消息都属于一个会话,因而第二个队列读取器在 RECEIVE 语句中等待。只要队列中的所有消息都属于同一会话,并且第二个队列读取器保持运行状态,队列监视器就不会再启动别的队列读取器。

确定是否发生激活

一旦 Service Broker 确定激活是必需的,Service Broker 就必须决定是否发生激活。

对于内部激活,当正在运行的程序的数量小于为队列设置的 MAX_QUEUE_READERS 值时,队列监视器将激活一个新的激活存储过程实例。如果正在运行的程序的数量等于或大于此 MAX_QUEUE_READERS 值,则队列监视器不会启动新的激活存储过程实例。管理视图 sys.dm_broker_activated_tasks (Transact-SQL) 包含有关由 Service Broker 启动的存储过程的信息。

对于外部应用程序,Service Broker 不具有可能正在处理队列的非重复队列读取器数量的有关信息。此外,在引发激活事件的时间和读取器开始读取队列的时间之间可能存在一段启动时间。因此,Service Broker 提供了外部应用程序响应超时值。在此超时值内,Service Broker 将不会生成新的通知。一旦应用程序对队列调用 RECEIVE 或者超时时间已过,则 Service Broker 将在需要激活时创建新的事件通知。外部应用程序在程序运行期间监视事件通知,以确定是否需要更多队列读取器来读取事件。