Jobb újrapróbálkozások exponenciális hátralépés és Jitter segítségével
1. Áttekintés
Ebben az oktatóanyagban azt vizsgáljuk meg, hogyan javíthatjuk az ügyfelek újrapróbálkozásait két különböző stratégiával: exponenciális hátralépés és jitter.
2. Próbálja újra
Elosztott rendszerben a számos komponens közötti hálózati kommunikáció bármikor meghiúsulhat. Az ügyfélalkalmazások végrehajtásával kezelik ezeket a hibákatújrapróbálkozik.
Tegyük fel, hogy van egy kliens alkalmazásunk, amely távoli szolgáltatást hív meg - a PingPongService.
interfész PingPongService {String hívás (String ping) dobja a PingPongServiceException; }
Az ügyfélalkalmazásnak újra kell próbálkoznia, ha a PingPongService visszatér a PingPongServiceException. A következő szakaszokban megvizsgáljuk az ügyfélpróbálkozások megvalósításának módjait.
3. Resilience4j Próbálja újra
Például a Resilience4j könyvtárat fogjuk használni, különösen annak újrapróbálkozási modulját. Hozzá kell adnunk a resilience4j-retry modult a modulunkhoz pom.xml:
io.github.resilience4j resilience4j-próbálkozzon újra
Az újrapróbálkozások frissítéséhez ne felejtse el átnézni a Resilience4j útmutatónkat.
4. Exponenciális hátralépés
Az ügyfélalkalmazásoknak felelősségteljesen kell végrehajtaniuk az újrapróbálkozásokat. Amikor az ügyfelek várakozás nélkül próbálkoznak újra a sikertelen hívásokkal, túlterhelhetik a rendszert, és hozzájáruljon a már szorongatott helyzetű szolgáltatás további romlásához.
Az exponenciális hátralépés egy általános stratégia a sikertelen hálózati hívások újrapróbálkozásának kezelésére. Egyszerűen, az ügyfelek fokozatosan hosszabb intervallumokat várnak az egymást követő ismétlések között:
wait_interval = alap * szorzó ^ n
hol,
- bázis a kezdeti intervallum, vagyis várja meg az első újrapróbálkozást
- n a bekövetkezett hibák száma
- szorzó tetszőleges szorzó, amely bármilyen megfelelő értékkel helyettesíthető
Ezzel a megközelítéssel légzési teret biztosítunk a rendszernek, hogy felépüljön az időszakos kudarcokból vagy még súlyosabb problémákból.
A Resilience4j újrapróbálkozásában használhatjuk az exponenciális visszalépés algoritmust annak konfigurálásával IntervalFunction hogy elfogad egy kezdetiInterval és a szorzó.
A IntervalFunction az újrapróbálási mechanizmus alvási funkcióként használja:
IntervalFunction intervalFn = IntervalFunction.ofExponentialBackoff (INITIAL_INTERVAL, MULTIPLIER); RetryConfig retryConfig = RetryConfig.custom () .maxAtt kísérletek (MAX_RETRIES) .intervalFunction (intervalFn) .build (); Újrapróbálkozás = Retry.of ("pingpong", retryConfig); Funkció pingPongFn = Újrapróbálkozás .decorateFunction (újrapróbálkozás, ping -> service.call (ping)); pingPongFn.apply ("Hello");
Szimuláljunk egy valós forgatókönyvet, és tegyük fel, hogy több kliensünk hívja a PingPongService egyidejűleg:
ExecutorService végrehajtók = newFixedThreadPool (NUM_CONCURRENT_CLIENTS); Feladatok listája = nCopies (NUM_CONCURRENT_CLIENTS, () -> pingPongFn.apply ("Hello")); végrehajtók.invokeAll (feladatok);
Nézzük meg a távoli meghívási naplókat NUM_CONCURRENT_CLIENTS egyenlő 4-vel:
[thread-1] 00: 37: 42.756 [thread-2] 00: 37: 42.756 [thread-3] 00: 37: 42.756 [thread-4] 00: 37: 42.756 [thread-2] 00: 37: 43.802 [menet-4] 00: 37: 43.802 [menet-1] 00: 37: 43.802 [menet-3] 00: 37: 43.802 [menet-2] 00: 37: 45.803 [ thread-1] 00: 37: 45.803 [menet-4] 00: 37: 45.803 [thread-3] 00: 37: 45.803 [thread-2] 00: 37: 49.808 [thread-3] 00 : 37: 49.808 [4. menet] 00: 37: 49.808 [1. menet] 00: 37: 49.808
Itt világos mintát láthatunk - az ügyfelek exponenciálisan növekvő intervallumokra várnak, de mindegyikük pontosan ugyanabban az időben hívja a távoli szolgáltatást minden egyes újrapróbálkozáskor (ütközés).
A probléma csak egy részével foglalkoztunk - a távoli szolgáltatást már nem próbáljuk újrapróbálkozásokkal, de ahelyett, hogy a munkaterhelést idővel elosztanánk, a munkaidőszakokat több üresjárati idővel tarkítottuk. Ez a viselkedés hasonló a Mennydörgő csorda problémához.
5. A Jitter bemutatása
Korábbi megközelítésünk szerint az ügyfél várakozása fokozatosan hosszabb, de még mindig szinkronizált. A jitter hozzáadása lehetővé teszi az ügyfelek közötti szinkronizálás megszakítását, elkerülve ezzel az ütközéseket. Ebben a megközelítésben véletlenszerűséget adunk a várakozási időközökhöz.
wait_interval = (alap * 2 ^ n) +/- (random_interval)
hol, random_interval hozzáadva (vagy kivonva) a kliensek közötti szinkronizálás megszakításához.
Nem fogunk foglalkozni a véletlenszerű intervallum kiszámításának mechanikájával, de a randomizálásnak el kell engednie a csúcsokat az ügyfélhívások sokkal egyenletesebb elosztásához.
A Resilience4j újrapróbálkozásánál használhatjuk az exponenciális hátrányt jitterrel egy exponenciális véletlenszerű visszalépés konfigurálásával IntervalFunction hogy elfogadja a randomizationFactor:
IntervalFunction intervalFn = IntervalFunction.ofExponentialRandomBackoff (INITIAL_INTERVAL, MULTIPLIER, RANDOMIZATION_FACTOR);
Térjünk vissza a valós forgatókönyvhöz, és nézzük meg a távoli meghívási naplókat jitterrel:
[thread-2] 39: 21.297-nél [thread-4] 39: 21.297-nél [thread-3] 39: 21.297-nél [thread-1] 39: 21.297-nél [thread-2] 39: 21.918-on [thread-3] At 39: 21.868 [thread-4] at 39: 22.011 [thread-1] at 39: 22.184 [thread-1] at 39: 23.086 [thread-5] 39: 23.939 [thread-3] 39: 24.152 [ thread-4] 39: 24.977 [thread-3] 39: 26.861 [thread-1] 39: 28.617 [thread-4] 39: 28.942 [thread-2] 39: 31.039
Most sokkal jobban elterjedtünk. Nekünk van kiküszöböli mind az ütközéseket, mind a tétlen időt, és szinte állandó ügyfélhívásokkal jár, kizárva a kezdeti túlfeszültséget.
Megjegyzés: Túlértékeltük a szemléltetés intervallumát, és valós helyzetekben kisebb hézagokkal rendelkeznénk.
6. Következtetés
Ebben az oktatóanyagban megvizsgáltuk, hogyan javíthatjuk az ügyfélalkalmazások újrapróbálkozását a sikertelen hívásokon az exponenciális hátrányok kibővítésével a jitterrel.
Az oktatóanyagban használt minták forráskódja elérhető a GitHubon.