2014-11-26 7 views
13

Występują problemy z wydajnością na naszej stronie związane z wysokim wykorzystaniem procesora. Podczas korzystania z profilera zidentyfikowaliśmy konkretną metodę, która zajmuje około 35 sekund.Powiadomienie SagePay (gateway płatności) w aplikacji ASP.Net MVC zwraca długi czas.

Jest to metoda oddzwonienia, gdy używana jest bramka płatności o nazwie SagePay.

mam skopiowane dwie metody, które są częścią tej rozmowy poniżej:

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(); 
    } 

Sposób GetTransactionByVendorTxCode jest proste wezwanie Entity Framework, więc już orzekł, że na zewnątrz.

Czy ktoś ma w tym jakieś doświadczenie lub czy widzi coś rażąco nie tak z kodem, który mógłby spowodować taki problem?

EDYCJA: Patrząc na tabelę podziału dostarczone przez profilera, mówi, że 99,6% czasu spędza w System.Web.Mvc.MvcHandler.BeginProcessRequest().

EDYCJA: Używając narzędzia do profilowania New Relic, mówi, że 22% całego czasu przetwarzania jest wydawane w metodzie this.sagePayTransactionManager.GetTransactionByVendorTxCode (vendorTxCode). Jest to po prostu zawierające wywołanie EF6 do repozytorium. Wywołanie zawiera jednak parametr predykatu, a nie predefiniowany warunek. Czy to możliwe, że zapytanie nie jest wstępnie skompilowane?

+0

"Proste wywołanie Entity Framework" - Jesteś pewien? jaki jest kod? –

+0

Spójrz na to pytanie/odpowiedź i dodaj kod writeline debugowania do Application_BeginRequest: http://stackoverflow.com/a/17073158/82333 –

+2

Zmień wywołanie GetTransactionByVendorTxCode na fałszywą konstrukcję danych i zobacz, czy czas reakcji zmienia się drastycznie . Jeśli tak, to masz wąskie gardło, jeśli nie czas na inne miejsce. – Bardo

Odpowiedz

1

Istnieje wiele rzeczy, które należy rozważyć tutaj.

Jeśli GetTransactionByVendorTxCode odpowiada za 22% całkowitego czasu przetwarzania, konieczne będzie usprawnienie wszystkich metod, które zostaną zastosowane, a następnie kontynuowanie eksploatacji innych wąskich gardeł w całym procesie przetwarzania.

Mówisz, że ta metoda powoduje wywołanie EF6 i przekazuje je w wyrażeniu predykatu, które zostanie użyte w klauzuli Where w celu utworzenia ostatecznego zapytania.

Jeśli zapytanie jest złożone, czy rozważasz delegowanie do StoredProcedure? Ponieważ zwracasz Entity, możesz zawiesić go z DbSet. (W przypadku DTO zawiesza się właściwość Database obiektu DbContext).

Należy również sprawdzić indeksy kolumn użytych w predykacie. Jaki jest obecny rekord? Czy Twoje zapytania skutkują szukaniem lub skanowaniem? Będziesz musiał przyjrzeć się wynikowym planom zapytania; jeśli za pomocą SQL Server uruchomić kwerendy Doradca dostrajania aparatu bazy danych.

Być może trochę więcej szczegółów na temat bieżącej konfiguracji pomoże zapewnić lepsze wskazówki.

2

Oto mój pierwszy krok do uzyskania roztworu:

umieszczone w początku czasowego przed tym stwierdzeniem a następnie zatrzymać go, gdy kończy. Powiedz nam czas.

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

Put w inny timer dla tego bloku kodu: Opowiedz nam czas względnego stosunku do metody powyżej.

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(); 
    } 

Wreszcie umieścić inny timer tutaj:

context.ApplicationInstance.CompleteRequest(); 

opublikować informację z powrotem do nas, a ja Cię do następnego kroku. To, co robimy powyżej, polega na uzyskiwaniu danych, które obejmą zarówno lokalny, jak i zdalny dostęp, aby znaleźć poważny problem. Najpierw wybieramy to, a potem dalej, jeśli to konieczne. Po prostu powiedz nam, jakie są wymiary.

+1

Klasa, której należy użyć dla tego rodzaju czasu, to System.Diagnostics.Stopwatch.StartNew(), ponieważ często uzyskuje o wiele dokładniejszą synchronizację czasu niż użycie DateTime.Now() –

+0

Jakieś aktualizacje wyników? –

Powiązane problemy