【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#代码:
/// 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文件
<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>