Vezetőválasztási minta

Azure Blob Storage

Az elosztott alkalmazásokban az együttműködő példányok gyűjteményei által végrehajtott műveletek koordinálhatók, ha megválaszt egy példányt vezetőnek, amely így felelős lesz a többi példány kezeléséért. Ez segít annak biztosításában, hogy a példányok nem ütköznek egymással, nem versengenek a megosztott erőforrásokért, és véletlenül sem zavarják meg a többi példány működését.

Kontextus és probléma

Egy tipikus felhőalkalmazásban több, koordinált módon működő feladat található. Ezek a feladatok lehetnek ugyanazt a kódot futtató és ugyanahhoz az erőforráshoz hozzáférést igénylő példányok, vagy előfordulhat, hogy egy összetett számítás egyes részeit végzik párhuzamosan.

Előfordulhat, hogy a feladatpéldányok általában külön futnak, de szükség lehet az egyes példányok műveleteinek koordinálására is, hogy ne ütközhessenek, ne okozzanak versengést a megosztott erőforrásokért, vagy véletlenül zavarják a más feladatpéldányok által végzett munkát.

Példa:

  • A horizontális skálázást alkalmazó felhőalapú rendszerekben egy feladat több példánya is futhat egyszerre úgy, hogy mindegyik példány egy különböző felhasználót szolgál ki. Ha ezek a példányok egy megosztott erőforrásba írnak, össze kell hangolni a műveleteiket, nehogy az egyik példány felülírja egy másik példány által elvégzett módosításokat.
  • Ha a feladatok egy összetett számítás egyes elemeit párhuzamosan végzik, a feladatok befejezése után összesíteni kell az eredményeket.

A feladatpéldányok társviszonyban állnak, ezért nincs koordinátorként vagy összesítőként működő, vezető példány.

Megoldás

Ki kell választani egy feladatpéldányt, amely vezetőként működik, és ez a példány fogja koordinálni a többi, alárendelt feladatpéldány műveleteit. Ha az összes feladatpéldány ugyanazt a kódot futtatja, akkor bármelyik működhet vezetőként. Ezért a választási folyamatot körültekintően kell kezelni annak érdekében, hogy két vagy több példány egyidejűleg átvehesse a vezető pozíciót.

A rendszernek robusztus mechanizmust kell biztosítania a vezető kiválasztásához. Ennek a módszernek tudnia kell kezelni az olyan eseményeket, mint például a hálózati kimaradások vagy a folyamathibák. Számos megoldásban az alárendelt folyamatpéldányok valamilyen szívveréses módszerrel vagy lekérdezéssel monitorozzák a vezetőt. Ha a kijelölt vezető váratlanul leáll, vagy az alárendelt feladatpéldányok hálózatkimaradás miatt nem tudják elérni, új vezetőt kell választani nekik.

Többféle módon is kiválasztható egy vezető egy elosztott környezet feladatainak készletéből, például:

  • A legalacsonyabb besorolású példány- vagy folyamatazonosítóval rendelkező feladatpéldány kiválasztása.
  • Verseny a közös, elosztott mutex beszerzéséért. Az a feladatpéldány lesz a vezető, amelyik elsőként szerzi be a mutexet. Azonban a rendszernek biztosítania kell a mutex felszabadítását, ha a vezető leáll vagy megszakad a kapcsolata a rendszer többi részével, hogy egy másik feladatpéldány vehesse át a vezető szerepet.
  • Egyik széles körben használt vezetőválasztási algoritmus (például a Bully algoritmus vagy a Ring algoritmus) megvalósítása. Ezek az algoritmusok azt feltételezik, hogy a választásban részt vevő minden jelölt egyedi azonosítóval rendelkezik, és megbízhatóan kommunikál a többi jelölttel.

Problémák és megfontolandó szempontok

A minta megvalósítása során az alábbi pontokat vegye figyelembe:

  • A vezetőválasztási folyamatnak ellenállónak kell lennie az átmeneti és állandó hibákkal szemben.
  • Észlelnie kell, ha a vezető leáll vagy bármely más okból elérhetetlenné válik (például egy kommunikációs hiba miatt). Az észlelés szükséges sebessége a rendszertől függ. Egyes rendszerek rövid ideig vezető nélkül is működőképesek lehetnek, és ez alatt az idő alatt az átmeneti hiba kijavítható. Más esetekben előfordulhat, hogy azonnal észlelni kell a vezető leállását, és új választást kell indítani.
  • Az automatikus horizontális skálázást megvalósító rendszereknél a vezető eltávolítható, ha a rendszer leskálázza a számítási erőforrásokat, vagy néhányat leállít közülük.
  • Közös, elosztott mutex használata esetén függőségi kapcsolat áll fenn a mutexet biztosító külső szolgáltatással. Ez a szolgáltatás kritikus hibapont lehet. Ha ez bármilyen okból elérhetetlenné válik, a rendszer nem fog tudni vezetőt választani.
  • Egyetlen dedikált folyamat egyszerűen használható vezetőként. Ha azonban a folyamat leáll, jelentős késés léphet fel az újraindulásáig. Ez a késés hatással lehet a többi folyamat teljesítményére és válaszidejére, ha arra várnak, hogy a vezető koordináljon egy műveletet.
  • Az egyik vezetőválasztási algoritmus manuális megvalósítása biztosítja a legnagyobb rugalmasságot a kód finomhangolásához és optimalizáláshoz.
  • Kerülje el, hogy a vezető szűk keresztmetszet legyen a rendszerben. A vezető célja, hogy koordinálja az alárendelt feladatok munkáját, és nem feltétlenül kell részt vennie ebben a munkában – bár ezt meg kell tudnia tenni, ha a feladatot nem választják meg vezetőként.

Mikor érdemes ezt a mintát használni?

Akkor használja ezt a mintát, ha egy elosztott alkalmazásban található feladathoz (például egy felhőben üzemeltetett megoldáshoz) gondos koordináció szükséges, és nincs természetes vezető.

Nem érdemes ezt a mintát használni, ha:

  • Van természetes vezető vagy dedikált folyamat, amely mindig vezetőként működhet. Például lehetséges, hogy meg lehet valósítani egyetlen olyan folyamatot, amely a feladatpéldányokat koordinálja. Ha ez a folyamat meghibásodik, a rendszer le tudja állítani, és újra tudja indítani.
  • A feladatok egy kisebb terhelést jelentő módszerrel is összehangolhatók. Ha például egyszerűen több feladatpéldánynak kell koordinált hozzáférést biztosítani egy közös erőforráshoz, akkor célszerűbb optimista vagy pesszimista zárolással vezérelni a hozzáférést.
  • Egy külső megoldás megfelelőbb. Például az Apache Hadoopon alapuló Microsoft Azure HDInsight szolgáltatás az Apache Zookeeper szolgáltatásaival koordinálja a leképezési és csökkentési feladatokat, amelyek begyűjtik és összefoglalják az adatokat.

Számítási feladatok tervezése

Az építészeknek értékelniük kell, hogyan használható a leader-választási minta a számítási feladat kialakításában az Azure Well-Architected Framework pilléreiben foglalt célok és alapelvek kezelésére. Példa:

Pillér Hogyan támogatja ez a minta a pillércélokat?
A megbízhatósági tervezési döntések segítenek a számítási feladatnak ellenállóvá válni a hibás működéssel szemben, és biztosítani, hogy a hiba bekövetkezése után teljesen működőképes állapotba kerüljön. Ez a minta a munka megbízható átirányításával mérsékli a csomóponthibák hatását. A feladatátvételt konszenzusos algoritmusokkal is megvalósítja, ha egy vezető hibásan működik.

- RE:05 Redundancia
- RE:07 Öngyógyítás

Mint minden tervezési döntésnél, fontolja meg az ezzel a mintával bevezethető többi pillér céljaival szembeni kompromisszumokat.

Példa

A Vezetőválasztási minta a GitHubon) bemutatja, hogyan használható bérlet egy Azure Storage-blobon egy megosztott, elosztott mutex implementálására szolgáló mechanizmus biztosítására. Ez a mutex használható vezető választására a rendelkezésre álló feldolgozópéldányok egy csoportja között. A bérlet megszerzésének első példánya a vezető lesz, és addig marad a vezető, amíg fel nem oldja a bérletet, vagy nem tudja megújítani a bérletet. Más feldolgozópéldányok továbbra is figyelhetik a blobbérletet, ha a vezető már nem érhető el.

A blobbérlet egy blob exkluzív írási zárolása. Egy blob mindig csak egy bérlet tárgya lehet. A feldolgozópéldányok bérletet kérhetnek egy adott blobon keresztül, és akkor kapják meg a bérletet, ha egyetlen másik feldolgozópéldány sem rendelkezik bérletet ugyanazon a blobon. Ellenkező esetben a kérés kivételt fog eredményezni.

Ha szeretné elkerülni, hogy egy hibás vezetőpéldány határozatlan ideig megtartsa a bérletet, adjon meg egy élettartamot a bérlethez. Amikor ez lejár, a bérlet elérhetővé válik. Bár egy példány rendelkezik a bérletmel, kérheti a bérlet megújítását, és a bérletet egy további időszakra is megadhatja. A vezető példány folyamatosan megismételheti ezt a folyamatot, ha meg szeretné tartani a bérletet. További információ a blobbérletekről: Blobbérlet (REST API).

Az BlobDistributedMutex alábbi C#-példában szereplő osztály azt a RunTaskWhenMutexAcquired metódust tartalmazza, amely lehetővé teszi, hogy egy feldolgozópéldány megpróbáljon bérletet szerezni egy adott blobon keresztül. A rendszer egy BlobSettings objektumban adja át a blob adatait (név, tároló és tárfiók) a konstruktornak a BlobDistributedMutex objektum létrehozásakor (ez az objektum egy egyszerű struktúra, amelyet a mintakód is tartalmaz). A konstruktor elfogadja Task azt a kódot is, amelyet a feldolgozó példánynak futtatnia kell, ha sikeresen beszerzi a bérletet a blobon keresztül, és a vezetőt választja. Vegye figyelembe, hogy a bérlet beszerzésének kevésbé fontos részleteit kezelő kód egy különálló, BlobLeaseManager nevű segítőosztályban szerepel.

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

A fenti kódmintában szereplő RunTaskWhenMutexAcquired metódus meghívja a következő kódmintában látható RunTaskWhenBlobLeaseAcquired metódust a bérlet beszerzéséhez. A RunTaskWhenBlobLeaseAcquired metódus aszinkron módon fut. Ha a bérlet beszerzése sikeresen megtörtént, a munkavégző példány lett a vezető. A meghatalmazott célja a taskToRunWhenLeaseAcquired többi feldolgozópéldányt koordináló munka végrehajtása. Ha a bérlet nem szereződött be, egy másik feldolgozópéldányt választottak meg vezetőként, és az aktuális feldolgozópéldány alárendelt marad. Vegye figyelembe, hogy a TryAcquireLeaseOrWait metódus egy olyan segédmetódus, amely a BlobLeaseManager objektumot használja a bérlet beszerzéséhez.

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

A vezető által elindított feladat szintén aszinkron módon fut. Amíg ez a feladat fut, az alábbi kódmintában látható RunTaskWhenBlobLeaseAcquired metódus időnként megkísérli a bérlet megújítását. Ez segít biztosítani, hogy a feldolgozó példány maradjon a vezető. A mintamegoldásban a megújítási kérelmek közötti késés kisebb, mint a bérlet időtartamára megadott idő, hogy megakadályozza egy másik feldolgozópéldány vezetővé választását. Ha a megújítás bármilyen okból meghiúsul, a vezetőspecifikus feladat megszakad.

Ha a bérletet nem lehet megújítani, vagy a feladatot megszakítják (esetleg a feldolgozópéldány leállítása miatt), a bérlet felszabadul. Ezen a ponton ez vagy egy másik feldolgozópéldány megválasztható vezetőként. Az alábbi kódkivonat a folyamat ezen részét mutatja.

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

A KeepRenewingLease metódus egy másik olyan segédmetódus, amely a BlobLeaseManager objektumot használja a bérlet megújításához. A CancelAllWhenAnyCompletes metódus megszakítja az első két paraméterként megadott feladatokat. A következő diagram azt mutatja be, hogyan választható egy vezető és futtatható egy műveleteket koordináló feladat a BlobDistributedMutex osztály használatával.

Az 1. ábra a BlobDistributedMutex osztály funkcióit szemlélteti

Az alábbi példakód bemutatja, hogyan használhatja az BlobDistributedMutex osztályt egy feldolgozópéldányon belül. Ez a kód egy bérletet szerez be egy, a bérlet tárolójában, az Azure Blob Storage-ban elnevezett MyLeaderCoordinatorTask blobon, és meghatározza, hogy a MyLeaderCoordinatorTask metódusban definiált kódnak futnia kell, ha a feldolgozópéldányt a vezetőnek választják.

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

Vegye figyelembe a következő szempontokat a mintaként szolgáló megoldásról:

  • A blob egy potenciálisan hibaérzékeny pont. Ha a blobszolgáltatás elérhetetlenné válik vagy elérhetetlenné válik, a vezető nem fogja tudni megújítani a bérletet, és más feldolgozópéldány nem fogja tudni beszerezni a bérletet. Ebben az esetben egyetlen feldolgozópéldány sem fog tudni vezetőként működni. Azonban a blobszolgáltatást úgy tervezték, hogy rugalmas legyen, ezért a blobszolgáltatás teljes leállása nagyon valószínűtlen.
  • Ha a vezető leáll, a vezető továbbra is megújíthatja a bérletet, megakadályozva, hogy bármely más feldolgozópéldány megszerezze a bérletet, és átvehesse a vezető pozíciót a tevékenységek koordinálása érdekében. A való világban a vezető állapotát gyakori időközönként ellenőrizni kell.
  • A választási folyamat nem determinált. Nem lehet feltételezni, hogy melyik feldolgozópéldány szerzi be a blobbérletet, és melyik lesz a vezető.
  • A blobbérlet céljaként használt blobot ne használja más célra. Ha egy feldolgozópéldány ebben a blobban próbál adatokat tárolni, ezek az adatok csak akkor lesznek elérhetők, ha a feldolgozópéldány a vezető, és a blobbérletet tárolja.

Következő lépések

Az alábbi útmutatók segíthetnek a minta megvalósításakor: