Yeniden deneme düzeniRetry pattern

Bir uygulamanın bir hizmete veya ağa bağlanmayı denerken karşılaştığı geçici hataları, başarısız olmuş bir işlemi saydam bir şekilde yeniden deneyerek işlemesine olanak tanıyın.Enable an application to handle transient failures when it tries to connect to a service or network resource, by transparently retrying a failed operation. Bu, uygulamanın kararlılığını iyileştirebilir.This can improve the stability of the application.

Bağlam ve sorunContext and problem

Bulutta çalışan öğelerle iletişim kuran bir uygulama, bu ortamda oluşabilecek geçici hataları duyarlı olmalıdır.An application that communicates with elements running in the cloud has to be sensitive to the transient faults that can occur in this environment. Bileşen ve hizmetlerle ağ bağlantısının anlık olarak kaybedilmesi, bir hizmetin geçici olarak kullanım dışı kalması veya bir hizmet meşgul olduğunda gerçekleşen zaman aşımları bu hatalar arasında yer alır.Faults include the momentary loss of network connectivity to components and services, the temporary unavailability of a service, or timeouts that occur when a service is busy.

Bu hatalar genellikle kendi kendine düzelir ve hataya yol açan eylem uygun gecikme süresi dolunca yinelenirse başarılı olma olasılığı yüksektir.These faults are typically self-correcting, and if the action that triggered a fault is repeated after a suitable delay it's likely to be successful. Örneğin, çok sayıda eşzamanlı isteği işleyen bir veritabanı hizmeti, iş yükü hafifleyene kadar geçici olarak ek istekleri reddeden bir azaltma stratejisi uygulayabilir.For example, a database service that's processing a large number of concurrent requests can implement a throttling strategy that temporarily rejects any further requests until its workload has eased. Veritabanına erişmeye çalışan bir uygulama bağlanamayabilir, ancak gecikme süresi dolunca yeniden denerse başarılı olabilir.An application trying to access the database might fail to connect, but if it tries again after a delay it might succeed.

ÇözümSolution

Bulutta, geçici hatalar nadir değildir ve uygulamalar bunları zarif ve saydam bir biçimde işleyecek şekilde tasarlanmalıdır.In the cloud, transient faults aren't uncommon and an application should be designed to handle them elegantly and transparently. Bu, hataların uygulama tarafından gerçekleştirilen iş görevleri üzerindeki etkilerini en aza indirir.This minimizes the effects faults can have on the business tasks the application is performing.

Bir uygulama uzak bir hizmete istek göndermeye çalışırken bir hata algılarsa, hatayı aşağıdaki stratejileri kullanarak işleyebilir:If an application detects a failure when it tries to send a request to a remote service, it can handle the failure using the following strategies:

  • İptal etme.Cancel. Hatanın geçici olmadığı veya yinelenirse başarılı sonuç alma olasılığının düşük olduğu belirtiliyorsa uygulama işlemi iptal etmeli ve bir özel durum bildirmelidir.If the fault indicates that the failure isn't transient or is unlikely to be successful if repeated, the application should cancel the operation and report an exception. Örneğin, geçersiz kimlik bilgileri sağlanmasından kaynaklanan bir kimlik doğrulama hatası alındığında, girişim sayısından bağımsız olarak başarılı sonuç alma olasılığı yoktur.For example, an authentication failure caused by providing invalid credentials is not likely to succeed no matter how many times it's attempted.

  • Yeniden Deneme.Retry. Bildirilen hata sıra dışı veya nadirse, bir ağ paketinin aktarılırken bozulması gibi sıra dışı durumlardan kaynaklanıyor olabilir.If the specific fault reported is unusual or rare, it might have been caused by unusual circumstances such as a network packet becoming corrupted while it was being transmitted. Böyle durumlarda aynı hatanın yinelenme olasılığı düşük olduğundan ve istek muhtemelen başarılı olacağından uygulama başarısız olan isteği hemen yeniden deneyebilir.In this case, the application could retry the failing request again immediately because the same failure is unlikely to be repeated and the request will probably be successful.

  • Gecikme süresi dolunca yeniden deneme.Retry after delay. Hata bir veya daha fazla yaygın bağlantı veya yoğunluk hatasından kaynaklanıyorsa, ağ veya hizmetteki bağlantı sorunlarının düzeltilmesi veya iş kapsamının temizlenmesi için kısa bir süre geçmesi gerekebilir.If the fault is caused by one of the more commonplace connectivity or busy failures, the network or service might need a short period while the connectivity issues are corrected or the backlog of work is cleared. Uygulamanın isteği yeniden denemeden önce uygun bir süre beklemesi gerekir.The application should wait for a suitable time before retrying the request.

Daha yaygın geçici hatalar söz konusu olduğunda, yeniden deneme işlemleri arasındaki süre, uygulamanın birden çok örneğinden gelen istekleri mümkün olduğunca eşit olarak yayacak şekilde seçilmelidir.For the more common transient failures, the period between retries should be chosen to spread requests from multiple instances of the application as evenly as possible. Bu, meşgul bir hizmete aşırı yüklenmeye devam etme olasılığını azaltır.This reduces the chance of a busy service continuing to be overloaded. Bir uygulamanın birçok örneği sürekli yeniden deneme istekleriyle bir hizmete aşırı yüklenirse, hizmetin kurtarılması daha uzun sürer.If many instances of an application are continually overwhelming a service with retry requests, it'll take the service longer to recover.

İstek yine başarısız olursa, uygulama bir süre bekleyip başka bir girişimde bulunabilir.If the request still fails, the application can wait and make another attempt. Gerekirse, en fazla istek girişimi sayısına ulaşılana kadar, yeniden deneme girişleri arasındaki gecikme süresi artırılarak bu işlem yinelenebilir.If necessary, this process can be repeated with increasing delays between retry attempts, until some maximum number of requests have been attempted. Gecikme süresi, hatanın türüne ve ilgili süre içinde düzelme olasılığına bağlı olarak kademeli bir şekilde veya katlanarak artırılabilir.The delay can be increased incrementally or exponentially, depending on the type of failure and the probability that it'll be corrected during this time.

Aşağıdaki diyagramda, bu düzen kullanılarak barındırılan bir hizmetteki bir işlemin çağrılması gösterilmiştir.The following diagram illustrates invoking an operation in a hosted service using this pattern. Önceden tanımlanmış bir girişim sayısına ulaşılmasına rağmen istek başarısız olursa uygulama hatayı özel bir durum olarak ele almalı ve uygun şekilde işlemelidir.If the request is unsuccessful after a predefined number of attempts, the application should treat the fault as an exception and handle it accordingly.

Şekil 1: Yeniden deneme düzenini kullanarak barındırılan bir hizmetteki bir işlemi çağırma

Uygulama, uzak bir hizmete erişmeye yönelik tüm girişimleri, yukarıda listelenen stratejilerden biriyle eşleşen bir yeniden deneme ilkesi uygulayan kodda sarmalamalıdır.The application should wrap all attempts to access a remote service in code that implements a retry policy matching one of the strategies listed above. Farklı hizmetlere gönderilen istekler farklı ilkelere tabi olabilir.Requests sent to different services can be subject to different policies. Bazı satıcılar, uygulamanın en fazla yeniden deneme sayısını, yeniden deneme girişimleri arasındaki süreyi ve diğer parametreleri belirtebileceği yeniden deneme ilkelerini uygulayan kitaplıklar sağlar.Some vendors provide libraries that implement retry policies, where the application can specify the maximum number of retries, the time between retry attempts, and other parameters.

Uygulamalar hataların ve başarısız işlemlerin ayrıntılarını günlüğe kaydetmelidir.An application should log the details of faults and failing operations. Bu bilgiler işleçler için yararlıdır.This information is useful to operators. Bir hizmet sık sık kullanılamaz hale geliyor veya meşgul oluyorsa çoğu zaman bunun nedeni hizmetin kaynaklarını tüketmiş olmasıdır.If a service is frequently unavailable or busy, it's often because the service has exhausted its resources. Hizmetin ölçeğini genişleterek bu hataların sıklığını azaltabilirsiniz.You can reduce the frequency of these faults by scaling out the service. Örneğin, bir veritabanı hizmeti sürekli olarak aşırı yükleniyorsa veritabanını bölümlemek ve yükü birden çok sunucuya dağıtmak yararlı olabilir.For example, if a database service is continually overloaded, it might be beneficial to partition the database and spread the load across multiple servers.

Microsoft Entity Framework, veritabanı işlemlerinin yeniden denenmesine yönelik olanaklar sağlar.Microsoft Entity Framework provides facilities for retrying database operations. Ayrıca, çoğu Azure hizmeti ve istemci SDK'sı bir yeniden deneme mekanizması içerir.Also, most Azure services and client SDKs include a retry mechanism. Daha fazla bilgi için bkz. Belirli hizmetlere yönelik yeniden deneme kılavuzu.For more information, see Retry guidance for specific services.

Sorunlar ve dikkat edilmesi gerekenlerIssues and considerations

Bu düzeni nasıl uygulayacağınıza karar verirken aşağıdaki noktaları dikkate almalısınız.You should consider the following points when deciding how to implement this pattern.

Yeniden deneme ilkesi, uygulamanın iş gereksinimleri ve hatanın yapısı ile eşleşecek şekilde ayarlanmalıdır.The retry policy should be tuned to match the business requirements of the application and the nature of the failure. Kritik olmayan bazı işlemler için birkaç kez yeniden deneyerek uygulamanın aktarım hızını etkilemek yerine hızlı başarısız kılma daha iyidir.For some noncritical operations, it's better to fail fast rather than retry several times and impact the throughput of the application. Örneğin, uzak bir hizmete erişen etkileşimli bir web uygulamasında kısa aralıklarla az sayıda yeniden deneme sonrasında başarısız kılmak ve kullanıcıya uygun bir ileti (örneğin, “lütfen daha sonra yeniden deneyin”) görüntülemek daha iyidir.For example, in an interactive web application accessing a remote service, it's better to fail after a smaller number of retries with only a short delay between retry attempts, and display a suitable message to the user (for example, “please try again later”). Bir toplu iş uygulaması için girişimler arasındaki gecikme süresini katlamalı bir şekilde artırarak yeniden deneme girişimi sayısını artırmak daha uygun olabilir.For a batch application, it might be more appropriate to increase the number of retry attempts with an exponentially increasing delay between attempts.

Girişimler arasındaki gecikme süresinin çok az, yeniden deneme sayısının yüksek olduğu agresif bir yeniden deneme ilkesi, kapasite üst sınırında veya buna yakın bir seviyede çalıştığı için meşgul olan bir hizmetin performansını daha da düşürebilir.An aggressive retry policy with minimal delay between attempts, and a large number of retries, could further degrade a busy service that's running close to or at capacity. Uygulama sürekli başarısız olan bir işlemi gerçekleştirmeye çalışıyorsa, bu yeniden deneme ilkesi uygulamanın yanıt verme hızını da etkileyebilir.This retry policy could also affect the responsiveness of the application if it's continually trying to perform a failing operation.

Bir istek önemli sayıda yeniden deneme girişimine rağmen başarısız olursa, uygulamanın aynı kaynağa gönderilerek anında hata bildirimine yol açacak diğer istekleri önlemesi daha iyidir.If a request still fails after a significant number of retries, it's better for the application to prevent further requests going to the same resource and simply report a failure immediately. Süre dolduğunda, uygulama bir veya daha fazla isteğe kontrollü bir şekilde izin vererek bunların başarılı olup olmadığını denetleyebilir.When the period expires, the application can tentatively allow one or more requests through to see whether they're successful. Bu stratejiyle ilgili daha fazla ayrıntı için bkz. Devre Kesici düzeni.For more details of this strategy, see the Circuit Breaker pattern.

Uygulamanın bir kere etkili olup olmadığını göz önünde bulundurun.Consider whether the operation is idempotent. Bir kere etkili oluyorsa yeniden denemek güvenlidir.If so, it's inherently safe to retry. Aksi takdirde, yeniden deneme girişimleri işlemin birden fazla kez yürütülmesine ve istenmeyen yan etkilere neden olabilir.Otherwise, retries could cause the operation to be executed more than once, with unintended side effects. Örneğin, bir hizmet isteği alıp başarıyla işlemesine rağmen yanıt gönderemiyor olabilir.For example, a service might receive the request, process the request successfully, but fail to send a response. Bu noktada, yeniden deneme mantığı ilk isteğin alınmadığını varsayarak isteği yeniden gönderebilir.At that point, the retry logic might re-send the request, assuming that the first request wasn't received.

Bir hizmete yönelik istekler, hatanın türüne bağlı olarak farklı özel durumlara yol açan çeşitli nedenlerden dolayı başarısız olabilir.A request to a service can fail for a variety of reasons raising different exceptions depending on the nature of the failure. Bazı özel durumlar hızlı bir şekilde çözülebilecek bir hata olduğunu gösterirken, bazıları hatanın daha kalıcı olduğunu gösterebilir.Some exceptions indicate a failure that can be resolved quickly, while others indicate that the failure is longer lasting. Yeniden deneme ilkesinin özel durum türüne göre yeniden deneme girişimleri arasındaki süreyi ayarlaması yararlıdır.It's useful for the retry policy to adjust the time between retry attempts based on the type of the exception.

Bir işlemin parçası olan bir alt işlemin yeniden denenmesinin genel işlem tutarlılığını nasıl etkileyeceğini göz önünde bulundurun.Consider how retrying an operation that's part of a transaction will affect the overall transaction consistency. İşlem alt işlemlerinin başarı olasılığını en üst düzeye çıkarmak ve tüm işlem adımlarını geri alma gereksinimini azaltmak için yeniden deneme ilkesinde ince ayar yapın.Fine tune the retry policy for transactional operations to maximize the chance of success and reduce the need to undo all the transaction steps.

Yeniden deneme kodunun çeşitli hata koşullarında tam olarak test edildiğinden emin olun.Ensure that all retry code is fully tested against a variety of failure conditions. Uygulamanın performansını veya güvenilirliğini etkilemediğinden, hizmetlerde aşırı yüklenmeye yol açmadığından veya yarış durumlarına ya da performans sorunlarına neden olmadığından emin olun.Check that it doesn't severely impact the performance or reliability of the application, cause excessive load on services and resources, or generate race conditions or bottlenecks.

Yalnızca başarısız olan bir işlemin tam bağlamının anlaşıldığı durumlarda yeniden deneme mantığı uygulayın.Implement retry logic only where the full context of a failing operation is understood. Örneğin, yeniden deneme ilkesi içeren bir görev yeniden deneme ilkesi içeren başka bir görevi çağırdığında, bu ek yeniden deneme katmanı işlemlerde daha uzun gecikme yaşanmasına neden olabilir.For example, if a task that contains a retry policy invokes another task that also contains a retry policy, this extra layer of retries can add long delays to the processing. Düşük düzeyli görevi hızla başarısız kılınacak ve kendisini çağıran göreve hatanın nedenini bildirecek şekilde yapılandırmak daha iyi olabilir.It might be better to configure the lower-level task to fail fast and report the reason for the failure back to the task that invoked it. Daha sonra, bu üst düzey görev hatayı kendi ilkesine göre işleyebilir.This higher-level task can then handle the failure based on its own policy.

Uygulama, hizmetler veya kaynaklardaki temel sorunların tanımlanabilmesi için yeniden denemeye neden olan tüm bağlantı hataların günlüğe kaydedilmesi önemlidir.It's important to log all connectivity failures that cause a retry so that underlying problems with the application, services, or resources can be identified.

Bir hizmet veya kaynak için gerçekleşme olasılığı en yüksek hataları araştırarak bunların uzun süreli veya kalıcı olup olmadığını keşfedin.Investigate the faults that are most likely to occur for a service or a resource to discover if they're likely to be long lasting or terminal. Böyle durumlarda hatanın biz özel durum olarak işlenmesi daha iyidir.If they are, it's better to handle the fault as an exception. Uygulama özel durumu bildirebilir veya günlüğe kaydedebilir ve sonra alternatif bir hizmeti (varsa) çağırarak ya da daha düşük işlevsellik sunarak devam etmeye çalışabilir.The application can report or log the exception, and then try to continue either by invoking an alternative service (if one is available), or by offering degraded functionality. Uzun süreli hataları algılama ve işleme hakkında daha fazla bilgi için bkz. Devre Kesici düzeni.For more information on how to detect and handle long-lasting faults, see the Circuit Breaker pattern.

Bu düzenin kullanılacağı durumlarWhen to use this pattern

Bir uygulamanın uzak bir hizmetle etkileşim kurarken veya uzak bir kaynağa erişirken geçici hatalar yaşama olasılığı varsa bu düzeni kullanın.Use this pattern when an application could experience transient faults as it interacts with a remote service or accesses a remote resource. Bu hataların kısa süreli olması beklenir ve daha önce başarısız olmuş bir istek, sonraki bir denemede başarılı olabilir.These faults are expected to be short lived, and repeating a request that has previously failed could succeed on a subsequent attempt.

Bu düzen aşağıdaki durumlarda kullanışlı olmayabilir:This pattern might not be useful:

  • Bir hatanın uzun süreli olma olasılığı yüksekse, bu yapılandırma uygulamanın yanıt verme hızını etkileyebilir.When a fault is likely to be long lasting, because this can affect the responsiveness of an application. Uygulama, başarısız olma olasılığı yüksek olan bir isteği tekrarlamaya çalışarak zaman ve kaynak israfı yapıyor olabilir.The application might be wasting time and resources trying to repeat a request that's likely to fail.
  • Bir uygulamanın iş mantığındaki hatalardan kaynaklanan iç özel durumlar gibi geçici hatalardan kaynaklanmayan hataların işlenmesi için.For handling failures that aren't due to transient faults, such as internal exceptions caused by errors in the business logic of an application.
  • Bir sistemdeki ölçeklenebilirlik sorunlarını gidermek için alternatif bir yöntem olarak.As an alternative to addressing scalability issues in a system. Bir uygulamada sık sık meşgul hataları oluşuyorsa, bu genellikle erişilen hizmetin veya kaynağın ölçeğinin büyütülmesi gerektiğini gösteren bir işarettir.If an application experiences frequent busy faults, it's often a sign that the service or resource being accessed should be scaled up.

ÖrnekExample

C# dilindeki bu örnekte Yeniden Deneme düzeninin uygulaması gösterilmektedir.This example in C# illustrates an implementation of the Retry pattern. Aşağıda gösterilen OperationWithBasicRetryAsync metodu, TransientOperationAsync metodu aracılığıyla zaman uyumsuz olarak bir dış hizmeti çağırır.The OperationWithBasicRetryAsync method, shown below, invokes an external service asynchronously through the TransientOperationAsync method. TransientOperationAsync metodunun ayrıntıları hizmete özgüdür ve örnek koddan çıkarılmıştır.The details of the TransientOperationAsync method will be specific to the service and are omitted from the sample code.

private int retryCount = 3;
private readonly TimeSpan delay = TimeSpan.FromSeconds(5);

public async Task OperationWithBasicRetryAsync()
{
  int currentRetry = 0;

  for (;;)
  {
    try
    {
      // Call external service.
      await TransientOperationAsync();

      // Return or break.
      break;
    }
    catch (Exception ex)
    {
      Trace.TraceError("Operation Exception");

      currentRetry++;

      // Check if the exception thrown was a transient exception
      // based on the logic in the error detection strategy.
      // Determine whether to retry the operation, as well as how
      // long to wait, based on the retry strategy.
      if (currentRetry > this.retryCount || !IsTransient(ex))
      {
        // If this isn't a transient error or we shouldn't retry,
        // rethrow the exception.
        throw;
      }
    }

    // Wait to retry the operation.
    // Consider calculating an exponential delay here and
    // using a strategy best suited for the operation and fault.
    await Task.Delay(delay);
  }
}

// Async method that wraps a call to a remote service (details not shown).
private async Task TransientOperationAsync()
{
  ...
}

Bu metodu çağıran deyim, bir for döngüsü içinde sarmalanmış bir try/catch bloğu içinde yer alır.The statement that invokes this method is contained in a try/catch block wrapped in a for loop. TransientOperationAsync metoduna yönelik çağrı özel durum oluşturmadan başarılı olursa for döngüsünden çıkılır.The for loop exits if the call to the TransientOperationAsync method succeeds without throwing an exception. TransientOperationAsync metodu başarısız olursa catch bloğu hatanın nedenini inceler.If the TransientOperationAsync method fails, the catch block examines the reason for the failure. Hatanın geçici olduğu düşünülüyorsa kod kısa bir gecikme süresinin ardından işlemi yeniden dener.If it's believed to be a transient error the code waits for a short delay before retrying the operation.

Ayrıca, for döngüsü işlemin kaç kez gerçekleştirilmeye çalışıldığını izler ve kod üç kez başarısız olursa özel durumun daha uzun süreli olduğu varsayılır.The for loop also tracks the number of times that the operation has been attempted, and if the code fails three times the exception is assumed to be more long lasting. Özel durum geçici değilse veya uzun süreliyse catch işleyicisi bir özel durum oluşturur.If the exception isn't transient or it's long lasting, the catch handler throws an exception. Bu özel durum for döngüsünden çıkar ve OperationWithBasicRetryAsync metodunu çağıran kod tarafından yakalanmalıdır.This exception exits the for loop and should be caught by the code that invokes the OperationWithBasicRetryAsync method.

Aşağıda gösterilen IsTransient metodu, kodun çalıştırıldığı ortamla ilgili olan belirli özel durumları denetler.The IsTransient method, shown below, checks for a specific set of exceptions that are relevant to the environment the code is run in. Geçici özel durum tanımı, erişilen kaynaklara ve işlemin çalıştırıldığı ortama göre değişkenlik gösterir.The definition of a transient exception will vary according to the resources being accessed and the environment the operation is being performed in.

private bool IsTransient(Exception ex)
{
  // Determine if the exception is transient.
  // In some cases this is as simple as checking the exception type, in other
  // cases it might be necessary to inspect other properties of the exception.
  if (ex is OperationTransientException)
    return true;

  var webException = ex as WebException;
  if (webException != null)
  {
    // If the web exception contains one of the following status values
    // it might be transient.
    return new[] {WebExceptionStatus.ConnectionClosed,
                  WebExceptionStatus.Timeout,
                  WebExceptionStatus.RequestCanceled }.
            Contains(webException.Status);
  }

  // Additional exception checking logic goes here.
  return false;
}