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 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.