Öncü Seçimi düzeni

Azure Blob Storage

Bir örneği, diğer örneklerin yönetilmesinin sorumluluğunu üstlenen bir öncü olarak seçerek, dağıtılmış bir uygulamadaki işbirliği yapan bir örnekler koleksiyonu tarafından gerçekleştirilen eylemleri koordine edin. Bu sayede örnekler arasında çakışmalar yaşanması, paylaşılan kaynaklar hakkında çekişmeler oluşması veya örneklerin yanlışlıkla birbirlerinin yaptıkları işleri engellemesi önlenmiş olur.

Bağlam ve sorun

Tipik bir bulut uygulamasında koordineli bir şekilde yürütülen birçok görev bulunur. Bu görevlerin tümü, aynı kodu çalıştıran ve aynı kaynaklara erişmesi gereken örnekler olabilir veya karmaşık bir hesaplamanın ayrı bölümlerini gerçekleştirmek için paralel olarak birlikte çalışıyor olabilir.

Görev örnekleri çoğu zaman ayrı çalışabilir, ancak çakışmadığından, paylaşılan kaynaklar için çekişmeye neden olmadığından veya diğer görev örneklerinin gerçekleştirdiği çalışmayı yanlışlıkla müdahale etmediğinden emin olmak için her örneğin eylemlerini koordine etmek de gerekebilir.

Örneğin:

  • Yatay ölçeklendirme uygulayan bulut tabanlı bir sistemde, aynı anda aynı görevin birden fazla örneği çalışıyor ve her bir örnek farklı bir kullanıcıya hizmet veriyor olabilir. Bu örnekler paylaşılan bir kaynağa yazıyorsa bir örneğin diğer örneklerin yaptığı değişikliklerin üzerine yazmasını engellemek için tüm örneklerin eylemleri koordine edilmelidir.
  • Görevler karmaşık bir hesaplamanın ayrı ayrı öğelerini paralel bir şekilde gerçekleştiriyorsa tüm görevler tamamlandığında sonuçlarının bir araya getirilmesi gerekir.

Tüm görev örnekleri eş düzeyde olduğu için koordine edici veya birleştirici rolünü üstlenecek doğal bir öncü yoktur.

Çözüm

Tek bir görev örneği öncü rolü için seçilmeli ve bu örnek, diğer alt görev örneklerinin eylemlerini koordine etmelidir. Tüm görev örnekleri aynı kodu çalıştırıyorsa her birinin öncü rolünü üstlenmesi mümkündür. Bu nedenle, iki veya daha fazla örneğin aynı anda lider konumunu devralmasını önlemek için seçim süreci dikkatli bir şekilde yönetilmelidir.

Sistem, öncünün seçilmesine yönelik güçlü bir mekanizma sağlamalıdır. Bu yöntem, ağ kesintileri veya süreç başarısızlıkları gibi olaylarla başa çıkabilmelidir. Birçok çözümde alt görev örnekleri, öncüyü bir tür sinyal yöntemiyle ya da yoklama aracılığıyla izler. Belirlenmiş öncü beklenmedik şekilde sonlandırılırsa veya bir ağ hatası nedeniyle alt görev örnekleri öncüye ulaşamazsa alt görev örneklerinin yeni bir öncü seçmesi gerekir.

Dağıtılmış bir ortamda bir dizi görev arasından bir öncü seçmek için aşağıdaki gibi stratejiler kullanılabilir:

  • En düşük dereceli örnek veya işlem kimliğine sahip görev örneğini seçme.
  • Paylaşılan, dağıtılmış bir mutex’i elde etmek için yarışma. Mutex’i elde eden ilk görev örneği öncü haline gelir. Bununla birlikte, öncü sonlandırılırsa veya sistemin geri kalanıyla bağlantısı kesilirse başka bir görev örneğinin öncü haline gelebilmesine olanak tanımak için sistem mutex’in serbest bırakılmasını sağlamalıdır.
  • Kabadayı Algoritması veya Halka Algoritması gibi yaygın öncü seçimi algoritmalarından birini uygulama. Bu algoritmalar, seçime katılan her adayın benzersiz bir kimliği olduğunu ve diğer adaylarla güvenilir bir şekilde iletişim kurabildiğini varsayar.

Sorunlar ve dikkat edilmesi gerekenler

Bu düzenin nasıl uygulanacağına karar verirken aşağıdaki noktaları göz önünde bulundurun:

  • Öncü seçme sürecinin geçici ve kalıcı hatalara dayanıklı olması gerekir.
  • Öncü başarısız olduğunda veya başka bir şekilde kullanılamaz hale geldiğinde (örneğin bir iletişim hatası nedeniyle) bunun algılanabilmesi gerekir. Bu algılamanın ne kadar hızlı olması gerektiği sisteme bağlıdır. Bazı sistemler bir öncü olmadan kısa bir süre çalışmayı sürdürebilir ve geçici bir hata varsa bu sırada giderilebilir. Diğer bazı durumlarda öncü hatasının hemen algılanması ve yeni bir seçimin tetiklenmesi gerekli olabilir.
  • Yatay otomatik ölçeklendirme uygulayan bir sistemde, sistemin ölçeği küçültülürse ve bilgi işlem kaynaklarının bazıları kapatılırsa öncü sonlandırılabilir.
  • Paylaşılan, dağıtılmış bir mutex kullanılması, bu mutex’i sağlayan dış hizmete bağımlık doğurur. Söz konusu hizmet, bir tek hata noktası haline gelir. Bu hizmetten herhangi bir nedenle yararlanılamazsa sistem bir öncü seçemez.
  • Öncü olarak tek bir adanmış sürecin kullanılması basit bir yaklaşımdır. Ancak bu süreç başarısız olursa sürecin yeniden başlatılması ciddi miktarda gecikmeye yol açabilir. Diğer süreçler öncünün bir işlemi koordine etmesini bekliyorsa, ortaya çıkan gecikme bu süreçlerin performansını ve yanıt sürelerini etkileyebilir.
  • Öncü seçimi algoritmalarından birinin el ile uygulanması, kodun ayarlanması ve iyileştirilmesine yönelik en yüksek esnekliği sağlar.
  • Öncüyü sistemde bir performans sorunu haline getirmekten kaçının. Liderin amacı, alt görevlerin çalışmasını koordine etmektir ve bu işin kendisine katılması gerekmez; ancak görev lider olarak seçilmediyse bunu yapabilmesi gerekir.

Bu düzenin kullanılacağı durumlar

Bu düzeni, dağıtılmış bir uygulamadaki (bulutta barındırılan bir çözüm gibi) görevlerin dikkatle koordine edilmesi gerektiğinde ve hiçbir doğal öncü olmadığında kullanın.

Bu düzen aşağıdaki durumlarda kullanışlı olmayabilir:

  • Doğal bir öncü ya da her zaman öncü olarak hareket edebilecek adanmış bir işlem var. Örneğin, görev örneklerini koordine eden tekil bir süreç uygulanması mümkün olabilir. Bu süreç başarısız olursa veya durumu bozulursa sistem süreci kapatıp yeniden başlatabilir.
  • Daha basit yapılı bir yöntem kullanılarak görevler arasında koordinasyon sağlanabiliyor. Örneğin, tek gereken şey birkaç görev örneğinin paylaşılan bir kaynağa koordine bir şekilde erişebilmesiyse erişimi denetlemek için iyimser veya kötümser kilitleme kullanılması daha iyi bir çözümdür.
  • Bir üçüncü taraf çözümü daha uygun olacak. Örneğin, Microsoft Azure HDInsight hizmeti (Apache Hadoop tabanlı) eşlemeleri koordine etmek ve veri toplayıp özetleyen görevlerin sayısını azaltmak için Apache Zookeeper tarafından sağlanan hizmetleri kullanır.

İş yükü tasarımı

Bir mimar, Azure İyi Tasarlanmış Çerçeve yapılarında ele alınan hedefleri ve ilkeleri ele almak için lider seçimi düzeninin iş yükünün tasarımında nasıl kullanılabileceğini değerlendirmelidir. Örneğin:

Yapı Taşı Bu desen sütun hedeflerini nasıl destekler?
Güvenilirlik tasarımı kararları, iş yükünüzün arızaya karşı dayanıklı olmasına ve bir hata oluştuktan sonra tamamen çalışır duruma gelmesini sağlamaya yardımcı olur. Bu desen, işi güvenilir bir şekilde yeniden yönlendirerek düğüm arızalarının etkisini azaltır. Ayrıca lider arızalandığında konsensüs algoritmaları aracılığıyla yük devretme uygular.

- RE:05 Yedeklilik
- RE:07 Kendi kendini iyileştirme

Herhangi bir tasarım kararında olduğu gibi, bu desenle ortaya konulabilecek diğer sütunların hedeflerine karşı herhangi bir dengeyi göz önünde bulundurun.

Örnek

GitHub'da Öncü Seçimi örneği) paylaşılan, dağıtılmış bir mutex uygulamaya yönelik bir mekanizma sağlamak üzere Azure Depolama blobu üzerinde kiralamanın nasıl kullanılacağını gösterir. Bu mutex, kullanılabilir çalışan örnekleri grubu arasında bir öncü seçmek için kullanılabilir. Kirayı almak için ilk örnek öncü seçilir ve kirayı serbest bırakana veya kirayı yenileyemediği sürece öncü olarak kalır. Öncü artık kullanılabilir olmaması durumunda diğer çalışan örnekleri blob kiralamasını izlemeye devam edebilir.

Blob kiralama, bir bloba ilişkin özel bir yazma kilididir. Bir blobun herhangi bir anda yalnızca bir kiralayanı olabilir. Bir çalışan örneği belirtilen blob üzerinde kiralama isteyebilir ve aynı blob üzerinde başka bir çalışan örneğinin kiralaması yoksa kira verilir. Aksi takdirde istek bir özel durum oluşturur.

Hatalı bir öncü örneğin kirayı süresiz olarak tutmasını önlemek için kiralama için bir ömür belirtin. Bu süre dolduğunda blob kiralanabilir hale gelir. Ancak, bir örnek kirayı barındırsa da, kiranın yenilenmesini isteyebilir ve daha fazla süre için kira verilir. Öncü örnek, kiralamayı korumak istiyorsa bu işlemi sürekli olarak yineleyebilir. Blob kiralama hakkında daha fazla bilgi için bkz. Lease Blob (Blob Kiralama) (REST API).

BlobDistributedMutex Aşağıdaki C# örneğindeki sınıfı, bir çalışan örneğinin belirtilen blob üzerinden kira almayı denemesini sağlayan yöntemi içerirRunTaskWhenMutexAcquired. Blobun bilgileri (ad, kapsayıcı ve depolama hesabı) BlobDistributedMutex nesnesi oluşturulduğunda (Bu nesne, örnek koda dahil edilmiş basit bir yapıdır.) bir BlobSettings nesnesi içinde oluşturucuya geçirilir. Oluşturucu ayrıca blob üzerinden kirayı başarıyla alır ve öncü seçilirse çalışan örneğinin çalıştırması gereken koda başvuran bir Task kabul eder. Kiralama işlemiyle ilgili alt düzey ayrıntıları işleyen kodun BlobLeaseManager adlı ayrı bir yardımcı sınıfta uygulandığını unutmayın.

public class BlobDistributedMutex
{
  ...
  private readonly BlobSettings blobSettings;
  private readonly Func<CancellationToken, Task> taskToRunWhenLeaseAcquired;
  ...

  public BlobDistributedMutex(BlobSettings blobSettings,
           Func<CancellationToken, Task> taskToRunWhenLeaseAcquired, ... )
  {
    this.blobSettings = blobSettings;
    this.taskToRunWhenLeaseAcquired = taskToRunWhenLeaseAcquired;
    ...
  }

  public async Task RunTaskWhenMutexAcquired(CancellationToken token)
  {
    var leaseManager = new BlobLeaseManager(blobSettings);
    await this.RunTaskWhenBlobLeaseAcquired(leaseManager, token);
  }
  ...

Yukarıdaki kod örneğinde yer alan RunTaskWhenMutexAcquired yöntemi, kirayı almak için aşağıdaki kod örneğinde gösterilen RunTaskWhenBlobLeaseAcquired yöntemini çağırır. RunTaskWhenBlobLeaseAcquired yöntemi zaman uyumsuz olarak çalışır. Kira başarıyla alınırsa, çalışan örneği öncü seçilmiştir. Temsilcinin taskToRunWhenLeaseAcquired amacı, diğer çalışan örneklerini koordine eden çalışmayı gerçekleştirmektir. Kira alınmazsa, öncü olarak başka bir çalışan örneği seçilmiştir ve geçerli çalışan örneği bir alt örnek olarak kalır. TryAcquireLeaseOrWait yönteminin kiralamayı gerçekleştirmek için BlobLeaseManager nesnesini kullanan bir yardımcı yöntem olduğunu unutmayın.

  private async Task RunTaskWhenBlobLeaseAcquired(
    BlobLeaseManager leaseManager, CancellationToken token)
  {
    while (!token.IsCancellationRequested)
    {
      // Try to acquire the blob lease.
      // Otherwise wait for a short time before trying again.
      string? leaseId = await this.TryAcquireLeaseOrWait(leaseManager, token);

      if (!string.IsNullOrEmpty(leaseId))
      {
        // Create a new linked cancellation token source so that if either the
        // original token is canceled or the lease can't be renewed, the
        // leader task can be canceled.
        using (var leaseCts =
          CancellationTokenSource.CreateLinkedTokenSource(new[] { token }))
        {
          // Run the leader task.
          var leaderTask = this.taskToRunWhenLeaseAcquired.Invoke(leaseCts.Token);
          ...
        }
      }
    }
    ...
  }

Öncü tarafından başlatılan görev de zaman uyumsuz olarak çalışır. Bu görev yürütülürken, aşağıdaki kod örneğinde gösterilen RunTaskWhenBlobLeaseAcquired yöntemi de düzenli aralıklarla kirayı yenilemeyi dener. Bu, çalışan örneğinin öncü olmaya devam etmesini sağlamaya yardımcı olur. Örnek çözümde, başka bir çalışan örneğinin öncü seçilmesini önlemek için yenileme istekleri arasındaki gecikme, kiralama süresi boyunca belirtilen süreden daha kısadır. Yenileme herhangi bir nedenle başarısız olursa öncüye özgü görev iptal edilir.

Kira yenilenemezse veya görev iptal edilirse (çalışan örneğinin kapatılmasının bir sonucu olarak), kira serbest bırakılır. Bu noktada, öncü olarak bu veya başka bir çalışan örneği seçilebilir. Aşağıdaki kod parçası sürecin bu bölümünü göstermektedir.

  private async Task RunTaskWhenBlobLeaseAcquired(
    BlobLeaseManager leaseManager, CancellationToken token)
  {
    while (...)
    {
      ...
      if (...)
      {
        ...
        using (var leaseCts = ...)
        {
          ...
          // Keep renewing the lease in regular intervals.
          // If the lease can't be renewed, then the task completes.
          var renewLeaseTask =
            this.KeepRenewingLease(leaseManager, leaseId, leaseCts.Token);

          // When any task completes (either the leader task itself or when it
          // couldn't renew the lease) then cancel the other task.
          await CancelAllWhenAnyCompletes(leaderTask, renewLeaseTask, leaseCts);
        }
      }
    }
  }
  ...
}

KeepRenewingLease yöntemi, kirayı yenilemek için BlobLeaseManager nesnesini kullanan bir başka yardımcı yöntemdir. CancelAllWhenAnyCompletes yöntemi, ilk iki parametre olarak belirtilen görevleri iptal eder. Aşağıdaki diyagramda, BlobDistributedMutex sınıfı kullanılarak bir öncü seçilmesi ve işlemleri koordine eden bir görevin çalıştırılması gösterilmektedir.

Şekil 1 BlobDistributedMutex sınıfının işlevlerini göstermektedir

Aşağıdaki kod örneği, bir çalışan örneği içinde sınıfının nasıl kullanılacağını BlobDistributedMutex gösterir. Bu kod, kiranın kapsayıcı Azure Blob Depolama adlı MyLeaderCoordinatorTask bir blob üzerinden kira alır ve çalışan örneği öncü seçilirse yönteminde MyLeaderCoordinatorTask tanımlanan kodun çalıştırılması gerektiğini belirtir.

// Create a BlobSettings object with the connection string or managed identity and the name of the blob to use for the lease
BlobSettings blobSettings = new BlobSettings(storageConnStr, "leases", "MyLeaderCoordinatorTask");

// Create a new BlobDistributedMutex object with the BlobSettings object and a task to run when the lease is acquired
var distributedMutex = new BlobDistributedMutex(
    blobSettings, MyLeaderCoordinatorTask);

// Wait for completion of the DistributedMutex and the UI task before exiting
await distributedMutex.RunTaskWhenMutexAcquired(cancellationToken);

...

// Method that runs if the worker instance is elected the leader
private static async Task MyLeaderCoordinatorTask(CancellationToken token)
{
  ...
}

Örnek çözüm hakkında aşağıdaki noktalara dikkat edin:

  • Blob olası bir tek hata noktasıdır. Blob hizmeti kullanılamaz duruma gelirse veya erişilemezse, öncü kirayı yenileyemez ve başka bir çalışan örneği kiralamayı alamaz. Bu durumda, hiçbir çalışan örneği öncü olarak davranamaz. Ancak, blob hizmeti dayanıklı olacak şekilde tasarlanmıştır. Bu yüzden, blob hizmetinin tamamen başarısız olma olasılığı son derece düşüktür.
  • Öncü tarafından gerçekleştirilen görev durursa, öncü kirayı yenilemeye devam edebilir ve diğer çalışan örneklerinin kirayı almasını engelleyebilir ve görevleri koordine etmek için öncü konumu devralabilir. Gerçek dünyada öncünün sistem durumu sık aralıklarla denetlenmelidir.
  • Seçim işlemi belirlenimci değildir. Blob kirasını hangi çalışan örneğinin edineceği ve öncü olacağı konusunda hiçbir varsayımda bulunamazsınız.
  • Blob kiralamanın hedefi olarak kullanılan blob başka herhangi bir amaçla kullanılmamalıdır. Bir çalışan örneği bu blobda veri depolamayı denerse, çalışan örneği öncü olmadığı ve blob kirasını tutmadığı sürece bu verilere erişilemez.

Sonraki adımlar

Bu düzeni uygularken aşağıdaki yönergeler de yararlı olabilir: