SagePay(支付网关)通知返回在MVC应用程序中需要很长时间 ASP.Net

本文关键字:应用程序 Net ASP 长时间 MVC 网关 返回 通知 SagePay | 更新日期: 2023-09-27 18:32:15

我们的网站遇到了与高 CPU 使用率相关的性能问题。 使用探查器时,我们已经确定了一个需要 ~35 秒才能返回的特定方法。

这是使用名为SagePay的支付网关时的回调方法。

我已经复制了下面此调用中的两个方法:

 public void SagePayNotificationReturn()
    {
        string vendorTxCode = Request.Form["vendortxcode"];
        var sagePayTransaction = this.sagePayTransactionManager.GetTransactionByVendorTxCode(vendorTxCode);
        if (sagePayTransaction == null)
        {
            // Cannot find the order, so log an error and return error response
            int errorId = this.exceptionManager.LogException(System.Web.HttpContext.Current.Request, new Exception(string.Format("Could not find SagePay transaction for order {0}.", vendorTxCode)));
            ReturnResponse(System.Web.HttpContext.Current, StatusEnum.ERROR, string.Format("{0}home/error/{1}", GlobalSettings.SiteURL, errorId), string.Format("Received notification for {0} but the transaction was not found.", vendorTxCode));
        }
        else
        {
            // Store the response and respond immediately to SagePay
            sagePayTransaction.NotificationValues = sagePayTransactionManager.FormValuesToQueryString(Request.Form);
            this.sagePayTransactionManager.Save(sagePayTransaction);
            ReturnResponse(System.Web.HttpContext.Current, StatusEnum.OK, string.Format("{0}payment/processtransaction/{1}", GlobalSettings.SiteURL, vendorTxCode), string.Empty);
        }
    }
 private void ReturnResponse(HttpContext context, StatusEnum status, string redirectUrl, string statusDetail)
    {
        context.Response.Clear();
        context.Response.ContentEncoding = Encoding.UTF8;
        using (StreamWriter streamWriter = new StreamWriter(context.Response.OutputStream))
        {
            streamWriter.WriteLine(string.Concat("Status=", status.ToString()));
            streamWriter.WriteLine(string.Concat("RedirectURL=", redirectUrl));
            streamWriter.WriteLine(string.Concat("StatusDetail=", HttpUtility.HtmlEncode(statusDetail)));
            streamWriter.Flush();
            streamWriter.Close();
        }
        context.ApplicationInstance.CompleteRequest();
    }

GetTransactionByVendorTxCode 方法是一个简单的实体框架调用,所以我排除了它。

是否有人在这方面有任何经验,或者他们能否看到可能导致此类问题的代码存在任何明显错误?

编辑:查看分析器提供的细分表,它说99.6%的时间花在System.Web.Mvc.MvcHandler.BeginProcessRequest()上。

编辑:使用分析工具New Relic,它说所有处理时间的22%都花在this.sagePayTransactionManager.GetTransactionByVendorTxCode(vendorTxCode)方法上。 这只是包含对存储库的 EF6 调用。 不过,该调用确实包含谓词参数,而不是预定义的条件。 可能是查询没有预编译吗?

SagePay(支付网关)通知返回在MVC应用程序中需要很长时间 ASP.Net

这是我获得解决方案的第一步:

在此语句之前放入计时器开始,然后在完成时停止它。 告诉我们时间跨度。

var sagePayTransaction = this.sagePayTransactionManager.GetTransactionByVendorTxCode(vendorTxCode);

为此代码块放入另一个计时器:告诉我们与上述方法相比的相对时间。

   using (StreamWriter streamWriter = new StreamWriter(context.Response.OutputStream))
    {
        streamWriter.WriteLine(string.Concat("Status=", status.ToString()));
        streamWriter.WriteLine(string.Concat("RedirectURL=", redirectUrl));
        streamWriter.WriteLine(string.Concat("StatusDetail=", HttpUtility.HtmlEncode(statusDetail)));
        streamWriter.Flush();
        streamWriter.Close();
    }

最后在这里放另一个计时器:

context.ApplicationInstance.CompleteRequest();

将信息发回给我们,我将指导您进行下一步。 我们上面所做的是获取跨越本地和远程访问的指标,以查找主要问题。 我们将首先选择它,然后在需要时进一步取得进展。 只需告诉我们测量值是什么。

这里您可能需要考虑许多事项。

如果 GetTransactionByVendorTxCode 负责总处理时间的 22%,那么您将需要简化该方法涉及的所有内容,但随后仍需要继续在整个处理管道中捕获其他瓶颈。

您说该方法抽象了对 EF6 的调用,并且它传入了一个谓词表达式,该表达式在 Where 子句中用于构建最终查询。

如果查询很复杂,您是否考虑过委托给存储过程?由于您要返回实体,因此可以将其挂在 DbSet 上。 (在 DTO 的情况下,它将挂起 DbContext 的数据库属性)。

此外,您还需要查看谓词中使用的列上的索引。当前记录计数是多少? 您的查询是否会导致搜索或扫描? 您需要查看生成的查询计划;如果使用 SQL Server,请运行查询数据库引擎优化顾问。

也许有关您当前设置的更多详细信息将有助于提供更好的指导。