on tavallista, että ohjelmistojärjestelmät soittavat etäkutsuja ohjelmille, jotka toimivat eri prosesseissa, luultavasti eri koneilla verkon poikki. Yksi suurista eroista muistissa olevien puhelujen ja etäpuhelujen välillä on se, että etäpuhelut voivat epäonnistua tai roikkua ilman vastausta, kunnes jokin aikalisäraja on saavutettu. Mikä on pahempaa, jos sinulla on monta soittajaa reagoimattomalla toimittajalla,niin kriittiset resurssit voivat loppua, mikä johtaa useiden järjestelmien kaatumiseen. Erinomaisessa kirjassaan Release It Michael Nygard popularisoi Katkaisijakuvion estääkseen tällaisen katastrofaalisen kaskadin.
katkaisijan perusidea on hyvin yksinkertainen. Käärit suojatun funktiokutsun katkaisijakohteeseen, joka tarkkailee vikojen varalta. Kun viat saavuttavat tietyn kynnyksen, katkaisija laukeaa, ja kaikki muut virrankatkaisijaan tulevat puhelut palaavat virheen kanssa ilman, että suojattua puhelua soitetaan lainkaan. Yleensä haluat myös jonkinlaisen monitorihälytyksen, jos katkaisija laukeaa.
tässä on yksinkertainen esimerkki tästä käyttäytymisestä Rubyssa, joka suojaa aikakatkaisuilta.
asetin katkaisijan lohkolla (Lambda), joka on suojattu kutsu.
cb = CircuitBreaker.new {|arg| @supplier.func arg}
katkaisija tallentaa lohkon, alustaa erilaisia parametreja (kynnyksiä, aikakatkaisuja ja valvontaa varten) ja nollaa katkaisijan suljettuun tilaansa.
luokan Piirinmurtaja…
attr_accessor :invocation_timeout, :failure_threshold, :monitor def initialize &block @circuit = block @invocation_timeout = 0.01 @failure_threshold = 5 @monitor = acquire_monitor reset end
katkaisijan kutsuminen kutsuu taustalla olevaa lohkoa, jos piiri on suljettu, mutta palauttaa virheen, jos se on auki
# client code aCircuitBreaker.call(5)
luokan Piirinmurtaja…
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
jos saamme aikalisän, kasvatamme vikalaskuria, onnistuneet puhelut nollaavat sen takaisin nollaan.
luokan Piirinmurtaja…
def record_failure @failure_count += 1 @monitor.alert(:open_circuit) if :open == state end def reset @failure_count = 0 @monitor.alert :reset_circuit end
I määritetään katkaisijan tila vertaamalla vikalukua kynnykseen
luokan Piirinmurtaja…
def state (@failure_count >= @failure_threshold) ? :open : :closed end
tämä yksinkertainen katkaisija välttää suojatun puhelun soittamisen, kun piiri on auki, mutta tarvitsisi ulkoisen väliintulon sen nollaamiseen, kun asiat ovat taas hyvin. Tämä on järkevä lähestymistapa rakennusten sähkökatkaisijoiden kanssa, mutta ohjelmistojen katkaisijoiden osalta voimme saada katkaisijan itse havaitsemaan, jos taustalla olevat puhelut toimivat jälleen. Voimme toteuttaa tämän itsesäätökäyttäytymisen kokeilemalla suojattua puhelua uudelleen sopivan välin jälkeen ja nollaamalla katkaisijan, jos se onnistuu.
tällaisen katkaisijan luominen tarkoittaa kynnyksen lisäämistä nollauksen kokeilemiseen ja muuttujan asettamiseen pitämään viimeisen virheen ajankohtaa.
Luokka Resetcuitbreaker…
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
nyt on olemassa kolmas tila läsnä – puoliksi avoin – eli piiri on valmis tekemään todellisen puhelun kokeiluna nähdäkseen, onko ongelma korjattu.
Luokka Resetcuitbreaker…
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
pyydetty soittamaan puoliavoimessa tilassa johtaa koepuheluun, joka joko Nollaa katkaisijan, jos se onnistuu, tai käynnistää aikakatkaisun uudelleen, jos ei.
Luokka Resetcuitbreaker…
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
tämä esimerkki on yksinkertainen selittävä, käytännössä katkaisijat tarjoavat hyvän vähän lisää ominaisuuksia ja parametrisointia. Usein ne suojaavat erilaisia virheitä, jotka suojattu puhelu voisi nostaa, kuten verkkoyhteyden vikoja. Kaikki virheet eivät saa laukaista piiri, jotkut pitäisi heijastaa normaalia epäonnistumisia ja käsitellään osana säännöllistä logiikkaa.
kun liikennettä on paljon, voi olla ongelmia monien puhelujen kanssa, jotka vain odottavat alkuperäistä aikalisää. Koska etäpuhelut ovat usein hitaita, on usein hyvä idea laittaa jokainen puhelu eri säiettä käyttäen tulevaisuutta tai lupaus käsitellä tuloksia, kun ne tulevat takaisin. Piirtämällä nämä langat lanka-altaasta voit järjestää piirin rikkoutumisen, kun Lanka-allas on käytetty loppuun.
esimerkissä näkyy yksinkertainen tapa laukaista katkaisija — luku, joka nollautuu onnistuneessa puhelussa. Kehittyneempi lähestymistapa voisi tarkastella toistuvuus virheitä, kompastuminen kun saat, vaikkapa, 50% epäonnistumisprosentti. Sinulla voi myös olla erilaisia kynnysarvoja eri virheille, kuten kynnysarvo 10 aikakatkaisuille mutta 3 yhteyshäiriöille.
näyttämäni esimerkki on virrankatkaisija synkronisiin puheluihin, mutta katkaisijat ovat käyttökelpoisia myös asynkroniseen viestintään. Yleinen tekniikka tässä on laittaa kaikki pyynnöt jonoon, jonka toimittaja kuluttaa nopeudellaan – hyödyllinen tekniikka välttää palvelimien ylikuormitus. Tällöin piiri katkeaa jonon täyttyessä.
yksinään toimivat katkaisijat auttavat vähentämään todennäköisesti epäonnistuviin toimintoihin sidottuja resursseja. Vältät odottamasta aikakatkaisuja asiakkaalle, ja rikkoutunut piiri välttää kuormittamasta kamppailevaa palvelinta. Puhun täällä etäpuheluista, jotka ovat yleinen tapaus katkaisijoille, mutta niitä voidaan käyttää missä tahansa tilanteessa, jossa haluat suojata järjestelmän osia muiden osien vioilta.
katkaisijat ovat arvokas seurantapaikka. Kaikki muutokset katkaisijan tilassa on kirjattava ja katkaisijoiden on paljastettava tilansa yksityiskohdat syvempää seurantaa varten. Breaker käyttäytyminen on usein hyvä lähde varoituksia syvemmistä ongelmista ympäristössä. Operaatiohenkilöstön pitäisi pystyä laukaisemaan tai nollaamaan katkaisijat.
yksin toimivat katkaisijat ovat arvokkaita, mutta niitä käyttävien asiakkaiden on reagoitava katkaisijavirheisiin. Kuten minkä tahansa etäkutsun kanssa, sinun on harkittava, mitä tehdä epäonnistumisen sattuessa. Epäonnistuuko se suorittamassasi operaatiossa, vai onko olemassa kiertoteitä, joita voit tehdä? Luottokortin valtuutus voidaan laittaa jonoon käsitellä myöhemmin, epäonnistuminen saada joitakin tietoja voidaan lieventää näyttämällä joitakin vanhentuneita tietoja, jotka on tarpeeksi hyvä näyttää.
lisätietoja
Netflixin tekniikkablogissa on paljon hyödyllistä tietoa paljon palveluja tarjoavien järjestelmien luotettavuuden parantamisesta. Heidän Riippuvuuskomentonsa puhuu katkaisijoiden ja kierrevaraston käytöstä.
Netflixillä on avoimen lähdekoodin Hystrix, hienostunut työkalu hajautettujen järjestelmien latenssin ja vikatoleranssin käsittelyyn. Se sisältää katkaisijakuvion toteutuksen, jossa kierrepooliraja on
on muitakin avoimen lähdekoodin toteutuksia katkaisijakuviosta Ruby, Java, Grails Plugin, C#, AspectJ ja Scala
kuittaukset
Pavel Shpak huomasi ja ilmoitti virheestä esimerkkikoodissa