【问题标题】:Struggling to get async working on deployment in ASP.net努力在 ASP.net 中进行异步部署
【发布时间】:2017-04-03 21:39:12
【问题描述】:

代码在我的开发环境中运行良好,但在使用可扩展架构部署时,它似乎死锁了。

这里的目标是获取 API 请求队列以发送到 SendGrid,将它们分批并一次处理每个批次。

来自 ASHX 处理程序的第一次调用

public void ProcessRequest(HttpContext context)
{
    var result = Code.Helpers.Email.Sendgrid.Queue.Process().Result;
    if (result.Success)
    {

Queue.Process()

public static async Task<GenericMethodResult> Process()
{
    var queueItems = GetQueueItemsToProcess();
    var batches = BatchQueueItems(queueItems);

    foreach (var batch in batches)
    {
        var r = await batch.SendToSendGrid();
        if (r.StopBatch)
        {
            break;
        }
    }

    return new GenericMethodResult(true);
}

SendToSendGrid()

public async Task<SendGridAPIMethodResponse> SendToSendGrid()
{
    var r = new SendGridAPIMethodResponse();
    var json = API.Functions.CreateJSONData(this);
    var sg = new SendGridClient(Settings.Email.SendgridAPIKey);

    dynamic response;
    if (Action == Action.UpdateRecipient)
    {
        response = await sg.RequestAsync(SendGridClient.Method.PATCH, urlPath: "contactdb/recipients", requestBody: json);
    }

    string jsonResponse = response.Body.ReadAsStringAsync().Result;
    // Process response...

    return r;
}

我已经尽可能多地删除了代码。

有人能告诉我为什么这段代码在生产中会超时吗?

【问题讨论】:

  • 您似乎是直接从处理程序发送电子邮件。虽然这可行,但最好通过background job 发送电子邮件。如果电子邮件服务器(或本例中的 Web 服务器)很慢怎么办?或者如果它暂时关闭怎么办?可能更好地将电子邮件排队并能够重试发送等。
  • @Mason 它没有发送电子邮件,而是将联系人数据与他们的联系人 API 同步。它具有合理的速率限制,因此每分钟最多只能执行约 50 次。它被一个分布式锁锁定,所以如果再次调用它,它会在忙碌时简单地返回而不做任何事情。如果失败,它不会从队列中删除项目,稍后会重试。它被设置为 Azure 计划任务,每分钟调用一次此 URL。由于缓存,很难将其与网站本身分离。
  • 这仍然是一个外部服务调用。由你决定。我只是讨厌我的应用程序的用户仅仅因为其他一些 3rd 方服务缓慢/不可用而获得缓慢/糟糕的体验。
  • @Mason 如何降低用户体验?这是一个相当简单的队列处理器。队列引入的延迟对最终用户是不可见的。
  • 啊,我现在看到您已经更新了您的评论,即由于用户交互,它是由计划任务而不是通过 AJAX 调用的。好的,所以你是对的,在这种情况下它不会减慢用户的速度。但随之而来的问题是,为什么要通过 ASP.NET 站点中的处理程序公开功能?为什么不让计划任务直接运行代码或使用更适合 Azure 中的工作的东西?

标签: c# asp.net async-await deadlock


【解决方案1】:

SendToSendGrid() 中对 .Result 的阻塞调用导致了死锁,因为您正在混合异步调用和阻塞调用。

string jsonResponse = response.Body.ReadAsStringAsync().Result;

一直使用异步

var jsonResponse = await response.Body.ReadAsStringAsync();

并尽量避免在异步方法中混合阻塞调用。

您还应该考虑使用HttpTaskAsyncHandler 使您的处理程序异步。

public class MyHandler : HttpTaskAsyncHandler {
    public override async Task ProcessRequestAsync(HttpContext context) {
        var result = await Code.Helpers.Email.Sendgrid.Queue.Process();
        if (result.Success) {
            //..other code
        }
    }
}

【讨论】:

  • 这太棒了,帮了很多忙。我可以看到SendToSendGrid() 正在完成执行,但似乎某处仍然存在死锁。链条中还有其他可能导致这种情况的东西吗?不确定是否重要,但SendToSendGrid() 函数需要访问当前的 HTTPContext
  • 这是否包括 ASHX 处理程序本身?我很困惑为什么会阻塞,我想要 ASHX 处理程序做的就是运行这个函数。如果我可以避免异步我会,但 sendgrid api 似乎没有任何同步功能。
  • 感谢您,这一切似乎很难实现。有没有更简单的方法来做到这一点?我要做的只是向 SendGrid 的 API 发送一个请求,然后处理响应。它位于每 60 秒作为作业调用一次的处理程序中。
  • 谢谢,已更新答案以提及它来自 ASHX
  • 检查Answer by Stephen Cleary,为什么像WaitResult 这样的调用会导致异步调用挂起,有据可查的行为。由于 ASP.Net 入口点可以是异步的,所以 Await 是最好的选择,但它与控制台应用程序不同,主入口点不是异步的
猜你喜欢
  • 1970-01-01
  • 2014-12-08
  • 1970-01-01
  • 2018-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-24
相关资源
最近更新 更多