Upravit

Sdílet prostřednictvím


Vzor voleb vedoucího procesu

Azure Blob Storage

Koordinuje akce prováděné kolekcí spolupracujících instancí v distribuované aplikaci tak, že jako vedoucí zvolí jednu instanci, která přebírá odpovědnost za správu ostatních instancí. To může pomoct zajistit, aby instance navzájem nekomlikovaly, způsobovaly kolize sdílených prostředků nebo neúmyslně zasahovaly do práce, kterou provádějí jiné instance.

Kontext a problém

Typická cloudová aplikace má mnoho úkolů, které fungují koordinovaným způsobem. Všechny tyto úlohy můžou být instance se stejným kódem a vyžadují přístup ke stejným prostředkům nebo mohou souběžně spolupracovat na provádění jednotlivých částí složitého výpočtu.

Instance úloh se můžou po většinu času spouštět samostatně, ale může být také nutné koordinovat akce jednotlivých instancí, aby se zajistilo, že nejsou v konfliktu, způsobí kolize pro sdílené prostředky nebo omylem zasahují do práce, kterou provádějí jiné instance úloh.

Například:

  • V cloudovém systému, který implementuje horizontální škálování, může být spuštěno více instancí stejné úlohy současně s každou instancí obsluhující jiného uživatele. Pokud tyto instance zapisují do sdíleného prostředku, je nutné koordinovat jejich akce, aby se zabránilo přepsání změn provedených ostatními instancemi.
  • Pokud úkoly provádějí paralelně jednotlivé prvky komplexního výpočtu, musí být výsledky agregovány, jakmile jsou všechny dokončeny.

Instance úloh jsou všechny partnerské vztahy, takže neexistuje přirozený vedoucí, který může fungovat jako koordinátor nebo agregátor.

Řešení

Jedna instance úlohy by měla být zvolena tak, aby fungovala jako vedoucí instance, a tato instance by měla koordinovat akce ostatních podřízených instancí úkolů. Pokud všechny instance úloh používají stejný kód, jsou schopny fungovat jako vedoucí instance. Proto musí být proces voleb spravován pečlivě, aby se zabránilo tomu, že dvě nebo více instancí převezme pozici vedoucího procesu současně.

Systém musí poskytovat robustní mechanismus pro výběr vodicího znaku. Tato metoda se musí vypořádat s událostmi, jako jsou výpadky sítě nebo selhání procesů. V mnoha řešeních instance podřízených úloh monitorují vedoucí proces prostřednictvím nějakého typu metody prezenčních signálů nebo dotazováním. Pokud se určený vedoucí server neočekávaně ukončí nebo selhání sítě znepřístupní instancím podřízených úkolů vedoucí instanci, je nutné, aby zvolili nového vedoucího.

Existuje několik strategií pro výběr vedoucí skupiny úkolů v distribuovaném prostředí, mezi které patří:

  • Závodí se na získání sdíleného distribuovaného mutexu. První instance úlohy, která získá mutex, je vedoucí instancí. Systém však musí zajistit, aby se v případě ukončení vedoucí instance nebo odpojení od zbytku systému uvolnilo mutex, aby se mohla stát vedoucí instancí jiné úlohy. Tato strategie je ukázaná v následujícím příkladu.
  • Implementace jednoho z běžných algoritmů volby vedoucího programu, jako je Bully Algorithm, Raft Consensus Algorithm nebo Ring Algorithm. Tyto algoritmy předpokládají, že každý kandidát ve volbách má jedinečné ID a že může spolehlivě komunikovat s ostatními kandidáty.

Problémy a důležité informace

Při rozhodování o implementaci tohoto modelu zvažte následující body:

  • Proces zvolení vedoucího procesu by měl být odolný vůči přechodným a trvalým selháním.
  • Musí být možné zjistit, kdy vedoucí uživatel selhal nebo je jinak nedostupný (například kvůli selhání komunikace). Jak rychle je potřeba zjistit, je systém závislý. Některé systémy můžou být schopny krátce fungovat bez vedoucího znaku, během kterého může být opravena přechodná chyba. V jiných případech může být nutné okamžitě zjistit selhání vedoucího serveru a aktivovat nové volby.
  • V systému, který implementuje horizontální automatické škálování, může být vedoucí instance ukončena, pokud se systém škáluje zpět a vypne některé výpočetní prostředky.
  • Použití sdíleného distribuovaného mutexu zavádí závislost na externí službě, která poskytuje mutex. Služba představuje jediný bod selhání. Pokud z nějakého důvodu přestane být dostupný, systém nebude moct zvolit vedoucího uživatele.
  • Použití jednoho vyhrazeného procesu jako vedoucího procesu je jednoduchý přístup. Pokud ale proces selže, může během restartování dojít k významnému zpoždění. Výsledná latence může ovlivnit výkon a dobu odezvy jiných procesů, pokud čekají na koordinaci operace vedoucího procesu.
  • Ruční implementace jednoho z algoritmů volby vedoucího člověka poskytuje největší flexibilitu pro ladění a optimalizaci kódu.
  • Vyhněte se tomu, aby vedoucí bod byl kritickým bodem v systému. Účelem vedoucího je koordinovat práci podřízených úkolů a nemusí se nutně účastnit samotné práce – i když by to mělo být možné, pokud není úkol zvolen jako vedoucí.

Kdy použít tento vzor

Tento vzor použijte, když úlohy v distribuované aplikaci, jako je řešení hostované v cloudu, potřebují pečlivou koordinaci a neexistuje žádný přirozený vedoucí proces.

Tento model nemusí být užitečný, pokud:

  • Existuje přirozený vedoucí nebo vyhrazený proces, který může vždy fungovat jako vedoucí proces. Může být například možné implementovat jeden proces, který koordinuje instance úloh. Pokud tento proces selže nebo není v pořádku, systém ho může vypnout a restartovat.
  • Koordinaci mezi úkoly lze dosáhnout jednodušší metodou. Pokud například několik instancí úloh jednoduše potřebuje koordinovaný přístup ke sdílenému prostředku, lepším řešením je použít optimistické nebo pesimistické uzamčení pro řízení přístupu.
  • Řešení třetí strany, jako je Apache Zookeeper , může být efektivnějším řešením.

Návrh úloh

Architekt by měl vyhodnotit způsob použití modelu volby leadera v návrhu úlohy k řešení cílů a principů, které se týkají pilířů architektury Azure Well-Architected. Například:

Pilíř Jak tento model podporuje cíle pilíře
Rozhodnutí o návrhu spolehlivosti pomáhají vaší úloze stát se odolnou proti selhání a zajistit, aby se po selhání obnovila do plně funkčního stavu. Tento model zmírní účinek chybných funkcí uzlu tím, že spolehlivě přesměruje práci. Implementuje také převzetí služeb při selhání prostřednictvím algoritmů pro konsensus, když vedoucí funkce nefunguje.

- RE:05 Redundance
- RE:07 Samoopravení

Stejně jako u jakéhokoli rozhodnutí o návrhu zvažte jakékoli kompromisy proti cílům ostatních pilířů, které by mohly být s tímto vzorem zavedeny.

Příklad

Ukázka volby leadera na GitHubu ukazuje, jak pomocí zapůjčení objektu blob služby Azure Storage poskytnout mechanismus pro implementaci sdíleného distribuovaného mutexu. Tento mutex lze použít k volbě vedoucího procesu mezi skupinou dostupných instancí pracovních procesů. První instance, která získá zapůjčení, je zvolena vedoucí instancí a zůstane vedoucí instancí, dokud neuvolní zapůjčení nebo nepůjde obnovit zapůjčení. Ostatní instance pracovních procesů můžou dál monitorovat zapůjčení objektů blob v případě, že vedoucí instance už nebudou k dispozici.

Zapůjčení objektu blob je výhradní zámek zápisu nad objektem blob. Jeden objekt blob může být předmětem pouze jednoho zapůjčení v libovolném okamžiku. Instance pracovního procesu může požádat o zapůjčení zadaného objektu blob a udělí se zapůjčení, pokud žádná jiná instance pracovního procesu nemá zapůjčení nad stejným objektem blob. Jinak požadavek vyvolá výjimku.

Chcete-li zabránit selhání instance vedoucí instance zachování zapůjčení neomezeně dlouhou dobu, zadejte životnost zapůjčení. Po vypršení platnosti se zapůjčení zpřístupní. Zatímco však instance uchovává zapůjčení, může požádat o prodloužení zapůjčení a bude mu uděleno zapůjčení na další časové období. Instance vedoucí instance může tento proces průběžně opakovat, pokud chce zachovat zapůjčení. Další informace o zapůjčení objektu blob najdete v tématu Zapůjčení objektu blob (REST API).

Třída BlobDistributedMutex v příkladu jazyka C# níže obsahuje metodu RunTaskWhenMutexAcquired , která umožňuje instanci pracovního procesu pokusit se získat zapůjčení pro zadaný objekt blob. Podrobnosti objektu blob (název, kontejner a účet úložiště) se předávají konstruktoru v objektu BlobSettings při BlobDistributedMutex vytvoření objektu (tento objekt je jednoduchá struktura, která je součástí ukázkového kódu). Konstruktor také přijímá Task odkaz na kód, který má instance pracovního procesu spustit, pokud úspěšně získá zapůjčení objektu blob a je zvolen vedoucí. Všimněte si, že kód, který zpracovává podrobnosti nízké úrovně získání zapůjčení je implementován v samostatné pomocné třídě s názvem BlobLeaseManager.

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

Metoda RunTaskWhenMutexAcquired v ukázce kódu výše vyvolá metodu RunTaskWhenBlobLeaseAcquired zobrazenou v následující ukázce kódu, která skutečně získá zapůjčení. Metoda RunTaskWhenBlobLeaseAcquired běží asynchronně. Pokud se zapůjčení úspěšně získá, byla instance pracovního procesu zvolena vedoucí instancí. Účelem delegáta taskToRunWhenLeaseAcquired je provést práci, která koordinuje ostatní instance pracovního procesu. Pokud není zapůjčení získáno, byla jako vedoucí zvolena jiná instance pracovního procesu a aktuální instance pracovního procesu zůstává podřízená. Všimněte si, že TryAcquireLeaseOrWait metoda je pomocná metoda, která používá BlobLeaseManager objekt k získání zapůjčení.

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

Úloha spuštěná vodicí znakem také běží asynchronně. Zatímco je tato úloha spuštěná, RunTaskWhenBlobLeaseAcquired metoda zobrazená v následující ukázce kódu se pravidelně pokouší obnovit zapůjčení. To pomáhá zajistit, aby instance pracovního procesu zůstala vedoucí instancí. V ukázkovém řešení je prodleva mezi žádostmi o obnovení kratší než doba určená pro dobu trvání zapůjčení, aby se zabránilo zvolení jiné instance pracovního procesu vedoucí instancí. Pokud obnovení z nějakého důvodu selže, úloha specifická pro vedoucí úkol se zruší.

Pokud se zapůjčení nepodaří obnovit nebo se úloha zruší (pravděpodobně v důsledku vypnutí instance pracovního procesu), zapůjčení se uvolní. V tuto chvíli lze tuto nebo jinou instanci pracovního procesu zvolit jako vedoucí instanci. Následující extrakce kódu ukazuje tuto část procesu.

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

Tato KeepRenewingLease metoda je další pomocná metoda, která používá BlobLeaseManager objekt k obnovení zapůjčení. Metoda CancelAllWhenAnyCompletes zruší úlohy zadané jako první dva parametry. Následující diagram znázorňuje použití BlobDistributedMutex třídy k volbě vedoucího úkolu a spuštění úlohy, která koordinuje operace.

Obrázek 1 znázorňuje funkce třídy BlobDistributedMutex

Následující příklad kódu ukazuje, jak používat BlobDistributedMutex třídu v rámci instance pracovního procesu. Tento kód získá zapůjčení objektu blob pojmenovaného MyLeaderCoordinatorTask v kontejneru zapůjčení Azure Blob Storage a určí, že kód definovaný v MyLeaderCoordinatorTask metodě by se měl spustit, pokud je instance pracovního procesu zvolena vedoucí instancí.

// 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)
{
  ...
}

Všimněte si následujících bodů ukázkového řešení:

  • Objekt blob je potenciální kritickým bodem selhání. Pokud se služba Blob Service stane nedostupnou nebo je nedostupná, nebude vedoucí moct obnovit zapůjčení a nebude moct získat zapůjčení žádná jiná instance pracovního procesu. V tomto případě nebude moct žádná instance pracovního procesu fungovat jako vedoucí instance. Služba Blob Service je ale navržená tak, aby byla odolná, takže úplné selhání služby blob se považuje za velmi nepravděpodobné.
  • Pokud se úkol prováděný vedoucími zastavuje, vedoucí může pokračovat v obnovení zapůjčení, aby zabránil jakékoli jiné instanci pracovního procesu v získání zapůjčení a převzetí pozice vedoucího procesu za účelem koordinace úkolů. Ve skutečném světě by se měl stav vedoucího stavu kontrolovat v častých intervalech.
  • Proces voleb je nedeterministický. Nemůžete provést žádné předpoklady o tom, která instance pracovního procesu získá zapůjčení objektu blob a stane se vedoucí instancí.
  • Objekt blob použitý jako cíl zapůjčení objektu blob by se neměl používat pro žádný jiný účel. Pokud se instance pracovního procesu pokusí uložit data v tomto objektu blob, nebudou tato data přístupná, pokud instance pracovního procesu není vedoucí instancí a uchovává zapůjčení objektu blob.

Další kroky

Při implementaci tohoto modelu můžou být relevantní také následující pokyny: