我有一个使用Service Broker的应用程序是SQL2008。大约每天一次,数据库的性能开始受到明显的打击,并且我确定这是由于Service Broker引起的。如果我使用以下命令硬重置所有代理连接:

ALTER DATABASE [RegencyEnterprise] SET OFFLINE WITH ROLLBACK IMMEDIATE
ALTER DATABASE [RegencyEnterprise] SET ONLINE

然后性能将恢复到第二天左右的正常水平。我还注意到,当性能不佳时,运行以下查询将返回大量(当前大约为1000)处于STARTED_OUTBOUND状态的 session :
SELECT * FROM sys.conversation_endpoints

另外,以下查询不会在其中返回任何条目:
SELECT * FROM sys.dm_qn_subscriptions
SELECT * FROM sys.transmission_queue

在此查询返回的项目很多的地方,性能似乎还不错。唯一有问题的时间是当STARTED_OUTBOUND的连接处于这种状态时。

我对SQL Server 2008实例上的Service Broker所做的唯一配置是运行以下命令:
ALTER DATABASE RegencyEnterprise SET ENABLE_BROKER

深入研究SQL错误日志,我还发现该条目也超过1000次:
07/11/2013 01:00:02,spid27s,Unknown,The query notification dialog on conversation handle '{6DFE46F5-25E9-E211-8DC8-00221994D6E9}.' closed due to the following error: '<?xml version="1.0"?><Error xmlns="http://schemas.microsoft.com/SQL/ServiceBroker/Error"><Code>-8490</Code><Description>Cannot find the remote service &apos;SqlQueryNotificationService-cb4e7a77-58f3-4f93-95c1-261954d3385a&apos; because it does not exist.</Description></Error>'.

尽管我相信我可以通过在数据库中创建一个主 key 来解决此错误,但在整个日志中我也经常看到该错误十二次左右:
06/26/2013 14:25:01,spid116,Unknown,Service Broker needs to access the master key in the database '<Database name>'. Error code:26. The master key has to exist and the service master key encryption is required.

我认为这些错误的数量可能与停留在队列中的 session 数量有关。这是我用来订阅查询通知的C#代码:
private void EstablishSqlConnection(
    String storedProcedureName,
    IEnumerable<SqlParameter> parameters,
    Action sqlQueryOperation,
    String serviceCallName,
    Int32 timeout,
    params MultipleResult[] results)
{
    SqlConnection storeConnection = (SqlConnection) ((EntityConnection) ObjectContext.Connection).StoreConnection;
    try
    {
        using (SqlCommand command = storeConnection.CreateCommand())
        {
            command.Connection = storeConnection;
            storeConnection.Open();

            SqlParameter[] sqlParameters = parameters.ToArray();
            command.CommandText = storedProcedureName;
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.AddRange(sqlParameters);

            if (sqlQueryOperation != null)
            {
                // Register a sql dependency with the SQL query.
                SqlDependency sqlDependency = new SqlDependency(command, null, timeout);
                sqlDependency.OnChange += OnSqlDependencyNotification;
            }

            using (DbDataReader reader = command.ExecuteReader())
            {
                results.ForEach(result => result.MapResults(this, reader));
            }
        }
    }
    finally
    {
        storeConnection.Close();
    }
}

这是我处理通知的方式:
    public static void OnSqlDependencyNotification(object sender, SqlNotificationEventArgs e)
    {
        if (e.Info == SqlNotificationInfo.Invalid)
        {
            // If we failed to register the SqlDependency, log an error
            <Error is loged here...>

            // If we get here, we are not in a valid state to requeue the sqldependency. However,
            // we are on an async thread and should NOT throw an exception. Instead we just return
            // here, as we have already logged the error to the database.
            return;
        }

        // If we are able to find and remove the listener, invoke the query operation to re-run the query.
        <Handle notification here...>
    }

有谁知道什么可以导致经纪人的连接进入这种状态?还是我可以使用哪些工具来试图弄清是什么原因造成的?我目前只有一个正在注册其通知的Web服务器,因此我的情况并不太复杂。

更新:

好的,所以我从this post确定,错误“无法找到远程服务...,因为它不存在”是由于SqlDependency无法正确地对其自身进行清理。服务终止后,代理仍在尝试向我的应用程序发送通知。所以现在看来​​,我只需要找到一种方法即可在调用SqlDependency.Start()之前清除我的应用程序启动时未正确清除的所有内容,但是除了原始方法外,我没有找到其他方法以上,使数据库脱机,这是 Not Acceptable 。有谁知道要清理吗?

最佳答案

我找到了解决该问题的可接受方法。首先,我将代码从SqlDependency迁移到其他地方,现在我改用SqlNotificationRequest。这样做可以防止在意外的时间创建/销毁Broker队列和服务。

即使这样,当我的应用程序退出时,仍然有一些 session 没有被标记为已关闭,因为设置通知的原始端点不再存在。因此,每次服务器重新初始化代码时,我都会清除现有对话。

这项调整将我每天的连接数量从1000多个减少到必须手动终止的数量,一直减少到最大20个。我高度建议使用SqlNotificationRequest而不是SqlDependency。

10-05 22:31