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öbb stratégia is létezik arra, hogy vezetőt választson ki egy elosztott környezetben lévő feladatok közül, beleértve a következőket:
- 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. Ezt a stratégiát az alábbi példában mutatjuk be.
- Az egyik gyakori vezető választási algoritmus implementálása, például a Bully Algoritmus, a Raft Consensus Algoritmus vagy a Kör algoritmus. 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, például az Apache Zookeeper hatékonyabb megoldás lehet.
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 GitHub vezető választási mintája bemutatja, hogyan használható bérlet egy Azure Storage-blobon egy megosztott, elosztott mutex implementálásához. 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 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:
- Ez a minta egy letölthető mintaalkalmazást tartalmaz.
- Útmutató az automatikus skálázáshoz. A feladatot futtató gazdagépek példányai az alkalmazások terhelésének változásainak megfelelően elindíthatók vagy leállíthatók. Az automatikus skálázás segítségével a teljesítmény és az átviteli sebesség szinten tartható a feldolgozási csúcsidőszakban.
- A tevékenységalapú aszinkron minta.
- A Bully algoritmust ábrázoló példa.
- A Ring algoritmust ábrázoló példa.
- Az Apache Curator, amely az Apache ZooKeeper ügyfélkódtára.
- Az MSDN blobbérleteket (REST API) ismertető cikke.