云计算 频道

如何处理SQL Azure连接丢失问题

  IT168 微软云计算博客征文活动专稿使用SQL Azure时,需要在互联网上建立连接,因此需要做好准备处理连接被异常删除,已建立的连接包括:返回数据的连接,打开连接池或客户端变量缓存中的连接。当你连接到SQL Azure时,连接是有可能丢失的,处理连接丢失最好的办法是重新建立连接,然后重新执行失败的命令或查询。

  网络可靠性

  运行你客户端代码的机器与SQL Azure服务器之间的所有网络组件的质量都在微软的控制范围之外,互联网上的任何原因都可能导致你的会话连接断开,在Windows Azure中运行应用程序时,连接丢失的风险会大大降低,因为应用程序和数据库之间的距离减少了,有可能它们都在同一个数据中心。

  因网络问题致使会话连接断开时,SQL Azure不能给应用程序返回一个有意义的错误,因为会话已经终止了,但是,当重新使用这个连接时,你将获得一个10053错误。

  连接重试

  如果你在本地局域网内连接到单个SQL Server服务器,当它失效或进行例行停机维护时,你的应用程序将永久断开,但如果你不止一台SQL Server服务器,并且应用程序代码做了修改,当主服务器不可用时,应用程序就自动连接到备用服务器,这时只需很短暂的断开时间,SQL Azure的行为和一个具有冗余功能的SQL Server集群类似,SQL Azure Fabric管理系统中每个节点的健康状态,它要么通知节点的状态不健康,要么是节点已准备好脱机,Fabric将会自动将你的会话重新连接到另一个节点的副本数据库上。

  目前有些故障转移行为会导致会话意外终止,而客户端会接收到一个普通的网络断开错误,这种情况下最好的做法是重新连接,SQL Azure会自动将你连接到一个健康的数据库。

  SQL Azure资源管理

  与其它数据库类似,SQL Azure会在遇到错误,资源短缺和其它原因时会终止会话,在这些情况下,如果客户端连接有活动请求,SQL Azure总是尝试返回一个特定的错误,需要注意的是,如果没有挂起的请求,不是每次都能成功向客户端应用程序返回一个错误。例如,如果你通过 SQL Server Management Studio 连接数据库的时间超过了30分钟无活动请求,你的会话将会超时,因为无活动请求,SQL Azure就不能返回一个错误。

  在以下这些情况下,SQL Azure将会关闭一个已建立的连接:

  · 应用程序持有空闲连接的时间超过了30分钟;

  · 你去吃午餐,但你的 SQL Server Management Studio 连接超过了30分钟无活动。

  SQL Azure错误

  除了前面提到的10053错误,还有两个错误代码比较有用:

  · 40197:服务处理你的请求时遇到错误,请重试。

  · 40501:服务繁忙,请10秒后重试。

  处理连接丢失的代码

  下面的代码考虑到了互联网的延迟和连接被删除的可能:

  · 重新执行失败的命令或查询,时间配置在.config文件中;

  · 每次尝试连接的等待时间也配置在.config文件中;

  · 只处理应该重新尝试的异常,抛出另外的异常;

  · 提取异常屏幕,便于将来增加其他情景。

  C#代码:

    /// <summary>
    
/// Generic Code that takes and input and executes
    
/// a statement against the SQL Azure
    
/// </summary>
    
/// <param name="companyId"></param>
    static void DoSomething(Int32 companyId)
    {
        
// This is the retry loop, handling the retries session
        
// is done in the catch for performance reasons
        
for (Int32 attempt = 1; ; )
        {
            
// Create a SqlConnection Class, the connection isn't established
            // until the Open() method is called
            using (SqlConnection sqlConnection
= new SqlConnection(
                ConfigurationManager.ConnectionStrings[
"SQLAzure"].
                    ConnectionString))
            {
                try
                {
                    
// Open the connection
                    sqlConnection.Open();
    
                    
// Statement To Call
                    
String sql = @"SELECT Color FROM Source WHERE Id = @CompanyId";
    
                    SqlCommand sqlCommand
= new SqlCommand(sql, sqlConnection);
    
                    sqlCommand.Parameters.AddWithValue(
"@CompanyId", companyId);
    
                    using (SqlDataReader sqlDataReader
= sqlCommand.ExecuteReader())
                    {
                        
while (sqlDataReader.Read())
                        {
                            
/// Do Something Wtih Color
                        }
    
                        return;
                    }
                }
                catch (SqlException sqlException)
                {
                    
// Increment Trys
                    attempt
++;
    
                    
// Find Maximum Trys
                    Int32 maxRetryCount
= Int32.Parse(
                        ConfigurationManager.AppSettings[
"ConnectionRetrys"]);
    
                    
// Throw Error if we have reach the maximum number of retries
                    
if (attempt == maxRetryCount)
                        throw;
    
                    
// Determine if we should retry or abort.
                    
if (!RetryLitmus(sqlException))
                        throw;
                    
else
                        Thread.Sleep(ConnectionRetryWaitSeconds(attempt));
                }
            }
        }
    }
    
    static Int32 ConnectionRetryWaitSeconds(Int32 attempt)
    {
        Int32 connectionRetryWaitSeconds
= Int32.Parse(ConfigurationManager.
            AppSettings[
"ConnectionRetryWaitSeconds"])
            
* 1000;
    
        
// Backoff Throttling
        connectionRetryWaitSeconds
= connectionRetryWaitSeconds *
            (Int32)Math.Pow(
2, attempt);
    
        return (connectionRetryWaitSeconds);
    }
    
    
/// <summary>
    
/// Determine from the exception if the execution
    
/// of the connection should Be attempted again
    
/// </summary>
    
/// <param name="exception">Generic Exception</param>
    
/// <returns>True if a a retry is needed, false if not</returns>
    static
Boolean RetryLitmus(SqlException sqlException)
    {
        switch (sqlException.Number)
        {
            
// The service has encountered an error
            
// processing your request. Please try again.
            
// Error code %d.
            
case 40197:
            
// The service is currently busy. Retry
            
// the request after 10 seconds. Code: %d.
            
case 40501:
            
//A transport-level error has occurred when
            
// receiving results from the server. (provider:
            
// TCP Provider, error: 0 - An established connection
            
// was aborted by the software in your host machine.)
            
case 10053:
                return (
true);
        }
    
        return (
false);
    }

.config文件

    <configuration>
      
<connectionStrings>
        
<add name="SQLAzure" connectionString="Server=tcp:youserver.database.windows.net;
    Database=Test;User ID=login@server;Password=yourPassword;
    Trusted_Connection
=False;Encrypt=True;"/>
      </connectionStrings>
      
<appSettings>
        
<add key="ConnectionRetrys" value="4"/>
        
<add key="ConnectionRetryWaitSeconds" value="5"/>
      
</appSettings>
    
</configuration>
0
相关文章