我的webapi 2控制器中有一个Post方法,可以插入数据库,但通常需要在成功之前重试几秒钟。基本上,这会导致重试之间的大量睡眠。实际上,它正在运行下面的代码。我的问题是,这段代码是否正确,以便我可以同时运行数千个这样的代码并且不会耗尽我的iis页面池?
public async Task<HttpResponseMessage> Post()
{
try
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
Thread.Sleep(30000);
//InsertInTable(json);
}
catch (Exception ex)
{
throw ex;
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* 由Peter添加为试试Stephens对Await.Delay的建议。显示无法在catch中等待的错误。
public async Task<HttpResponseMessage> PostXXX()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
bool success = false;
int retriesMax = 30;
int retries = retriesMax;
while (retries > 0)
{
try
{
// DO ADO.NET EXECUTE THAT MAY FAIL AND NEED RETRY
retries = 0;
}
catch (SqlException exception)
{
// exception is a deadlock
if (exception.Number == 1205)
{
await Task.Delay(1000);
retries--;
}
// exception is not a deadlock
else
{
throw;
}
}
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
* 更多由Peter提供,试用Enterprise块,缺少类(未找到StorageTransientErrorDetectionStrategy类)
public async Task<HttpResponseMessage> Post()
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(2));
var retryPolicy =
new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);
try
{
// Do some work that may result in a transient fault.
retryPolicy.ExecuteAction(
() =>
{
// Call a method that uses Windows Azure storage and which may
// throw a transient exception.
Thread.Sleep(10000);
});
}
catch (Exception)
{
// All the retries failed.
}
*****导致SqlServer失控的开启连接的代码
try
{
await retryPolicy.ExecuteAsync(
async () =>
{
// this opens a SqlServer Connection and Transaction.
// if fails, rolls back and rethrows exception so
// if deadlock, this retry loop will handle correctly
// (caused sqlserver to spin out of control with open
// connections so replacing with simple call and
// letting sendgrid retry non 200 returns)
await InsertBatchWithTransaction(sendGridRecordList);
});
}
catch (Exception)
{
Utils.Log4NetSimple("SendGridController:POST Retries all failed");
}
和异步插入代码(有一些......&#39; s)
private static async Task
InsertBatchWithTransaction(List<SendGridRecord> sendGridRecordList)
{
using (
var sqlConnection =
new SqlConnection(...))
{
await sqlConnection.OpenAsync();
const string sqlInsert =
@"INSERT INTO SendGridEvent...
using (SqlTransaction transaction =
sqlConnection.BeginTransaction("SampleTransaction"))
{
using (var sqlCommand = new SqlCommand(sqlInsert, sqlConnection))
{
sqlCommand.Parameters.Add("EventName", SqlDbType.VarChar);
sqlCommand.Transaction = transaction;
try
{
foreach (var sendGridRecord in sendGridRecordList)
{
sqlCommand.Parameters["EventName"].Value =
GetWithMaxLen(sendGridRecord.EventName, 60);
await sqlCommand.ExecuteNonQueryAsync();
}
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
}
}
答案 0 :(得分:4)
没有。至少,您希望将Thread.Sleep
替换为await Task.Delay
。 Thread.Sleep
将阻止该请求上下文中的线程池线程,什么都不做。使用await
允许该线程返回到线程池以用于其他请求。
您可能还想考虑Transient Fault Handling Application Block。
更新:您无法在await
块中使用catch
;这是VS2013中C#语言的限制(下一个版本可能允许这样,as I note on my blog)。所以现在,你必须做这样的事情:
private async Task RetryAsync(Func<Task> action, int retries = 30)
{
while (retries > 0)
{
try
{
await action();
return;
}
catch (SqlException exception)
{
// exception is not a deadlock
if (exception.Number != 1205)
throw;
}
await Task.Delay(1000);
retries--;
}
throw new Exception("Retry count exceeded");
}
要使用瞬态故障处理应用程序块,首先要定义哪些错误是&#34;瞬态&#34; (应该重试)。根据您的示例代码,您只想在出现SQL死锁异常时重试:
private sealed class DatabaseDeadlockTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
{
public bool IsTransient(Exception ex)
{
var sqlException = ex as SqlException;
if (sqlException == null)
return false;
return sqlException.Number == 1205;
}
public static readonly DatabaseDeadlockTransientErrorDetectionStrategy Instance = new DatabaseDeadlockTransientErrorDetectionStrategy();
}
然后你可以这样使用它:
private static async Task RetryAsync()
{
var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2));
var retryPolicy = new RetryPolicy(DatabaseDeadlockTransientErrorDetectionStrategy.Instance, retryStrategy);
try
{
// Do some work that may result in a transient fault.
await retryPolicy.ExecuteAsync(
async () =>
{
// TODO: replace with async ADO.NET calls.
await Task.Delay(1000);
});
}
catch (Exception)
{
// All the retries failed.
}
}