CircuitBreaker

gyakori, hogy a szoftverrendszerek távoli hívásokat kezdeményeznek különböző folyamatokban futó szoftverekre, valószínűleg különböző gépeken a hálózaton keresztül. Az egyik nagy különbség a memóriában lévő hívások és a távoli hívások között az, hogy a távoli hívások meghiúsulhatnak, vagy válasz nélkül lóghatnak, amíg el nem érik az időkorlátot. Ami még rosszabb, ha sok hívója van egy nem reagáló szállítónál, akkor elfogyhatnak a kritikus erőforrások, amelyek több rendszerben lépcsőzetes hibákhoz vezetnek. Kiváló könyvében kiadás , Michael Nygard népszerűsítette a megszakító mintát, hogy megakadályozza az ilyen katasztrofális kaszkádot.

a megszakító alapötlete nagyon egyszerű. A védett funkcióhívást egy megszakító objektumba csomagolja, amely figyeli a hibákat. Miután a hibák elértek egy bizonyos küszöbértéket, a megszakító kiold, és minden további hívás a megszakítóhoz hibával tér vissza, anélkül, hogy a védett hívást egyáltalán megkezdenék. Általában valamilyen monitor riasztást is szeretne, ha a megszakító kiold.

Íme egy egyszerű példa erre a viselkedésre a Ruby – ban, amely védelmet nyújt az időkorlátok ellen.

a megszakítót egy blokkkal (Lambda) állítottam be, amely a védett hívás.

cb = CircuitBreaker.new {|arg| @supplier.func arg}

a megszakító tárolja a blokkot, inicializálja a különböző paramétereket (küszöbértékek, időtúllépések és felügyelet), és visszaállítja a megszakítót zárt állapotába.

osztályú áramkör-megszakító…

 attr_accessor :invocation_timeout, :failure_threshold, :monitor def initialize &block @circuit = block @invocation_timeout = 0.01 @failure_threshold = 5 @monitor = acquire_monitor reset end

a megszakító hívása felhívja a mögöttes blokkot, ha az áramkör zárva van, de hibát ad vissza, ha nyitva van

# client code aCircuitBreaker.call(5)

osztályú áramkör-megszakító…

 def call args case state when :closed begin do_call args rescue Timeout::Error record_failure raise $! end when :open then raise CircuitBreaker::Open else raise "Unreachable Code" end end def do_call args result = Timeout::timeout(@invocation_timeout) do @circuit.call args end reset return result end

ha időtúllépést kapunk, növeljük a hibaszámlálót, a sikeres hívások visszaállítják nullára.

osztályú áramkör-megszakító…

 def record_failure @failure_count += 1 @monitor.alert(:open_circuit) if :open == state end def reset @failure_count = 0 @monitor.alert :reset_circuit end

meghatározom a megszakító állapotát, összehasonlítva a hibaszámot a

osztályú áramkör-megszakító küszöbértékével…

 def state (@failure_count >= @failure_threshold) ? :open : :closed end

ez az egyszerű megszakító elkerüli a védett hívás kezdeményezését, amikor az áramkör nyitva van, de külső beavatkozásra lenne szükség a visszaállításhoz, amikor a dolgok újra jól vannak. Ez ésszerű megközelítés az épületek elektromos megszakítóival, de a szoftver megszakítók esetében a megszakító maga is észlelheti, hogy az alapul szolgáló hívások újra működnek-e. Ezt az Ön-visszaállítási viselkedést úgy valósíthatjuk meg, hogy megfelelő időköz után újra megpróbáljuk a védett hívást, és ha sikerül, visszaállítjuk a megszakítót.

az ilyen típusú megszakító létrehozása azt jelenti, hogy hozzáadunk egy küszöböt a visszaállítás kipróbálásához, és beállítunk egy változót az utolsó hiba idejének megtartására.

osztály ResetCircuitBreaker…

 def initialize &block @circuit = block @invocation_timeout = 0.01 @failure_threshold = 5 @monitor = BreakerMonitor.new @reset_timeout = 0.1 reset end def reset @failure_count = 0 @last_failure_time = nil @monitor.alert :reset_circuit end

most egy harmadik állapot van jelen – félig nyitva – ami azt jelenti, hogy az áramkör készen áll arra, hogy valódi hívást kezdeményezzen próbaként, hogy megnézze, hogy a probléma megoldódott-e.

osztály ResetCircuitBreaker…

 def state case when (@failure_count >= @failure_threshold) && (Time.now - @last_failure_time) > @reset_timeout :half_open when (@failure_count >= @failure_threshold) :open else :closed end end

a félig nyitott állapotban történő hívás kérése próbahívást eredményez, amely vagy visszaállítja a megszakítót, ha sikeres, vagy újraindítja az időtúllépést, ha nem.

osztály ResetCircuitBreaker…

 def call args case state when :closed, :half_open begin do_call args rescue Timeout::Error record_failure raise $! end when :open raise CircuitBreaker::Open else raise "Unreachable" end end def record_failure @failure_count += 1 @last_failure_time = Time.now @monitor.alert(:open_circuit) if :open == state end

ez a példa egyszerű magyarázó, a gyakorlatban a megszakítók jó kicsit több funkciót és paraméterezést biztosítanak. Gyakran védelmet nyújtanak a védett hívás által felvetett hibák, például a hálózati kapcsolat hibái ellen. Nem minden hibának kell kioldania az áramkört, néhánynak tükröznie kell a normál hibákat, és a szabályos logika részeként kell kezelni őket.

a sok forgalom, akkor problémái vannak a sok hívás csak arra vár, hogy a kezdeti időtúllépés. Mivel a távoli hívások gyakran lassúak, gyakran jó ötlet, ha minden hívást egy másik szálra helyezünk egy jövőbeli vagy ígéret felhasználásával, hogy kezeljük az eredményeket, amikor visszatérnek. Ha ezeket a szálakat egy szálkészletből húzza ki, gondoskodhat arról, hogy az áramkör megszakadjon, amikor a szálkészlet kimerült.

a példa egy egyszerű módot mutat be a megszakító kioldására — egy számlálás, amely visszaállítja a sikeres hívást. Egy kifinomultabb megközelítés megvizsgálhatja a hibák gyakoriságát, megbotlik, ha mondjuk 50% – os hibaarányt kap. Előfordulhat, hogy a különböző hibákhoz különböző küszöbértékek is vannak, például időtúllépés esetén 10, csatlakozási hibák esetén pedig 3 küszöbérték.

az általam bemutatott példa egy megszakító szinkron hívásokhoz, de a megszakítók aszinkron kommunikációhoz is hasznosak. Általános technika itt az, hogy az összes kérést sorba helyezzük, amelyet a szállító a sebességével fogyaszt – hasznos technika a szerverek túlterhelésének elkerülése érdekében. Ebben az esetben az áramkör megszakad, amikor a sor megtelik.

a megszakítók önmagukban segítenek csökkenteni a valószínűleg sikertelen műveletekben lekötött erőforrásokat. Kerülje az időtúllépések várakozását az ügyfél számára, a megszakadt áramkör pedig elkerüli a terhelést a küzdő szerveren. Itt a távoli hívásokról beszélek, amelyek a megszakítók általános esete, de bármilyen helyzetben felhasználhatók, amikor meg akarják védeni a rendszer egyes részeit a többi rész hibáitól.

a megszakítók értékes helyek a megfigyeléshez. A megszakító állapotának bármilyen változását naplózni kell, a megszakítóknak pedig fel kell tárniuk állapotuk részleteit a mélyebb megfigyelés érdekében. A megszakító viselkedése gyakran jó figyelmeztetésforrás a környezet mélyebb problémáira. Az Üzemeltető személyzetnek képesnek kell lennie a megszakítók kioldására vagy visszaállítására.

a megszakítók önmagukban értékesek, de az őket használó ügyfeleknek reagálniuk kell a megszakító hibáira. Mint minden távoli meghívásnál, meg kell fontolnia, hogy mit kell tennie hiba esetén. Nem sikerül a művelet, amit végez, vagy vannak olyan megoldások, amelyeket megtehet? A hitelkártya-engedélyezést sorba lehet állítani, hogy később foglalkozzon vele, az adatok megszerzésének elmulasztása enyhíthető azáltal, hogy néhány elavult adatot mutat, amely elég jó a megjelenítéshez.

további olvasmányok

a netflix tech blog sok hasznos információt tartalmaz a sok szolgáltatást nyújtó rendszerek megbízhatóságának javításáról. A függőségi parancsuk a megszakítók és a szálkészlet korlátjának használatáról szól.

a Netflix nyílt forráskódú Hystrix, egy kifinomult eszköz az elosztott rendszerek késleltetésének és hibatűrésének kezelésére. Ez magában foglalja a végrehajtása a megszakító minta a menet pool limit

vannak más nyílt forráskódú megvalósítások a megszakító minta Ruby, Java, Grails Plugin, C#, AspectJ, Scala

Köszönetnyilvánítás

Pavel Shpak hibát észlelt és jelentett a

példakódban

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.