Inleiding tot Hystrix

1. Overzicht

Een typisch gedistribueerd systeem bestaat uit een groot aantal services die samenwerken.

Deze services zijn vatbaar voor mislukking of vertraagde reacties. Als een service uitvalt, kan dit van invloed zijn op andere services die de prestaties beïnvloeden en mogelijk andere delen van de applicatie ontoegankelijk maken of in het ergste geval de hele applicatie uitschakelen.

Er zijn natuurlijk oplossingen beschikbaar die applicaties veerkrachtig en fouttolerant maken - een van die frameworks is Hystrix.

De Hystrix-frameworkbibliotheek helpt de interactie tussen services te regelen door fouttolerantie en latentietolerantie te bieden. Het verbetert de algehele veerkracht van het systeem door de falende services te isoleren en het trapsgewijze effect van fouten te stoppen.

In deze reeks berichten zullen we beginnen met te kijken hoe Hystrix te hulp schiet als een service of systeem uitvalt en wat Hystrix in deze omstandigheden kan bereiken.

2. Eenvoudig voorbeeld

De manier waarop Hystrix fout- en latentietolerantie biedt, is door oproepen naar externe services te isoleren en af ​​te wikkelen.

In dit eenvoudige voorbeeld verpakken we een gesprek in de rennen() methode van de Hystrix-opdracht:

klasse CommandHelloWorld breidt HystrixCommand {private String-naam; CommandHelloWorld (Stringnaam) {super (HystrixCommandGroupKey.Factory.asKey ("ExampleGroup")); this.name = naam; } @Override protected String run () {return "Hallo" + naam + "!"; }}

en we voeren de oproep als volgt uit:

@Test openbare leegte gegevenInputBobAndDefaultSettings_whenCommandExecuted_thenReturnHelloBob () {assertThat (nieuwe CommandHelloWorld ("Bob"). Execute (), equalTo ("Hallo Bob!")); }

3. Maven-instellingen

Om Hystrix in een Maven-project te gebruiken, hebben we hystrix-core en rxjava-core afhankelijkheid van Netflix in het project pom.xml:

 com.netflix.hystrix hystrix-core 1.5.4 

De laatste versie is hier altijd te vinden.

 com.netflix.rxjava rxjava-kern 0.20.7 

De laatste versie van deze bibliotheek is hier altijd te vinden.

4. Service op afstand instellen

Laten we beginnen met het simuleren van een voorbeeld uit de echte wereld.

In het onderstaande voorbeeld, de klas RemoteServiceTestSimulator vertegenwoordigt een service op een externe server. Het heeft een methode die reageert met een bericht na de opgegeven tijdsperiode. We kunnen ons voorstellen dat dit wachten een simulatie is van een tijdrovend proces op het externe systeem dat resulteert in een vertraagde reactie op de oproepende dienst:

klasse RemoteServiceTestSimulator {privé lang wachten; RemoteServiceTestSimulator (lang wachten) gooit InterruptedException {this.wait = wait; } String execute () gooit InterruptedException {Thread.sleep (wacht); retourneer "Succes"; }}

En hier is onze voorbeeldcliënt dat roept de RemoteServiceTestSimulator.

De oproep naar de dienst is geïsoleerd en verpakt in het rennen() methode van een HystrixCommand. Het is deze verpakking die de veerkracht biedt die we hierboven hebben besproken:

klasse RemoteServiceTestCommand breidt HystrixCommand uit {private RemoteServiceTestSimulator remoteService; RemoteServiceTestCommand (Setter config, RemoteServiceTestSimulator remoteService) {super (config); this.remoteService = remoteService; } @Override protected String run () genereert uitzondering {return remoteService.execute (); }}

De oproep wordt uitgevoerd door de uitvoeren () methode op een instantie van de RemoteServiceTestCommand voorwerp.

De volgende test laat zien hoe dit wordt gedaan:

@Test openbare leegte gegevenSvcTimeoutOf100AndDefaultSettings_whenRemoteSvcExecuted_thenReturnSuccess () gooit InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixCommandGroupKey.Factory); assertThat (nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (100)). execute (), equalTo ("Success")); }

Tot nu toe hebben we gezien hoe externe serviceaanroepen in het HystrixCommand voorwerp. Laten we in het onderstaande gedeelte bekijken hoe we moeten omgaan met een situatie waarin de service op afstand begint te verslechteren.

5. Werken met service op afstand en defensief programmeren

5.1. Defensief programmeren met time-out

Het is een algemene programmeerpraktijk om time-outs in te stellen voor oproepen naar externe diensten.

Laten we beginnen met te kijken hoe u de time-out kunt inschakelen HystrixCommand en hoe het helpt door kortsluiting:

@Test openbare leegte gegevenSvcTimeoutOf5000AndExecTimeoutOf10000_whenRemoteSvcExecuted_thenReturnSuccess () gooit InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixCommandGroupKey); HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter (); commandProperties.withExecutionTimeoutInMilliseconds (10_000); config.andCommandPropertiesDefaults (commandProperties); assertThat (nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (500)). execute (), equalTo ("Success")); }

In de bovenstaande test vertragen we de reactie van de service door de time-out in te stellen op 500 ms. We stellen ook de time-out voor de uitvoering in HystrixCommand 10.000 ms, waardoor er voldoende tijd is voor de service op afstand om te reageren.

Laten we nu eens kijken wat er gebeurt als de time-out voor de uitvoering minder is dan de time-out van de service:

@Test (verwacht = HystrixRuntimeException.class) openbare leegte gegevenSvcTimeoutOf15000AndExecTimeoutOf5000_whenRemoteSvcExecuted_thenExpectHre () gooit InterruptedException {HystrixCommand.Setter config = HystrixCommandey. HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter (); commandProperties.withExecutionTimeoutInMilliseconds (5_000); config.andCommandPropertiesDefaults (commandProperties); nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (15_000)). execute (); }

Merk op hoe we de lat hebben verlaagd en de time-out voor uitvoering hebben ingesteld op 5.000 ms.

We verwachten dat de service binnen 5.000 ms reageert, terwijl we hebben ingesteld dat de service na 15.000 ms reageert. Als u merkt dat wanneer u de test uitvoert, de test stopt na 5.000 ms in plaats van 15.000 ms te wachten en een HystrixRuntimeException.

Dit laat zien hoe Hystrix niet langer wacht dan de geconfigureerde time-out op een antwoord. Dit helpt het systeem beter te beschermen door Hystrix.

In de onderstaande secties zullen we kijken naar het instellen van de grootte van de draadpool om te voorkomen dat de draden uitgeput raken, en we zullen het voordeel ervan bespreken.

5.2. Defensief programmeren met beperkte threadpool

Het instellen van time-outs voor serviceoproepen lost niet alle problemen op die verband houden met externe services.

Wanneer een service op afstand traag begint te reageren, blijft een typische toepassing die service op afstand aanroepen.

De applicatie weet niet of de service op afstand in orde is of niet en er worden nieuwe threads gegenereerd elke keer dat er een verzoek binnenkomt. Dit zorgt ervoor dat threads op een server die al worstelt, worden gebruikt.

We willen niet dat dit gebeurt, omdat we deze threads nodig hebben voor andere externe oproepen of processen die op onze server worden uitgevoerd, en we willen ook voorkomen dat het CPU-gebruik toeneemt.

Laten we eens kijken hoe we de grootte van de threadpool kunnen instellen HystrixCommand:

@Test openbare leegte gegevenSvcTimeoutOf500AndExecTimeoutOf10000AndThreadPool_whenRemoteSvcExecuted _thenReturnSuccess () gooit InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixKeyCommand) (Remote.Factory); HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter (); commandProperties.withExecutionTimeoutInMilliseconds (10_000); config.andCommandPropertiesDefaults (commandProperties); config.andThreadPoolPropertiesDefaults (HystrixThreadPoolProperties.Setter () .withMaxQueueSize (10) .withCoreSize (3) .withQueueSizeRejectionThreshold (10)); assertThat (nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (500)). execute (), equalTo ("Success")); }

In de bovenstaande test stellen we de maximale wachtrijgrootte, de kernwachtrijgrootte en de afwijzingsgrootte van de wachtrij in. Hystrix zal beginnen met het afwijzen van de verzoeken wanneer het maximale aantal threads de 10 heeft bereikt en de takenwachtrij een grootte van 10 heeft bereikt.

De kerngrootte is het aantal threads dat altijd in leven blijft in de threadpool.

5.3. Defensief programmeren met kortsluitstroomonderbrekingspatroon

Er is echter nog steeds een verbetering die we kunnen aanbrengen bij serviceoproepen op afstand.

Laten we eens kijken naar het geval dat de service op afstand is begonnen te mislukken.

We willen er niet steeds om vragen en middelen verspillen. We zouden idealiter gedurende een bepaalde tijd willen stoppen met het indienen van verzoeken om de service de tijd te geven om te herstellen voordat we verzoeken hervatten. Dit is wat de Kortsluitstroomonderbreker patroon.

Laten we eens kijken hoe Hystrix dit patroon implementeert:

@Test openbare leegte gegevenCircuitBreakerSetup_whenRemoteSvcCmdExecuted_thenReturnSuccess () gooit InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixCommandGroupKey.Factory.asKey (") RemoteServiceGroup (") RemoteServiceGroup; HystrixCommandProperties.Setter-eigenschappen = HystrixCommandProperties.Setter (); properties.withExecutionTimeoutInMilliseconds (1000); properties.withCircuitBreakerSleepWindowInMilliseconds (4000); properties.withExecutionIsolationStrategy (HystrixCommandProperties.ExecutionIsolationStrategy.THREAD); properties.withCircuitBreakerEnabled (true); properties.withCircuitBreakerRequestVolumeThreshold (1); config.andCommandPropertiesDefaults (eigenschappen); config.andThreadPoolPropertiesDefaults (HystrixThreadPoolProperties.Setter () .withMaxQueueSize (1) .withCoreSize (1) .withQueueSizeRejectionThreshold (1)); assertThat (this.invokeRemoteService (config, 10_000), equalTo (null)); assertThat (this.invokeRemoteService (config, 10_000), equalTo (null)); assertThat (this.invokeRemoteService (config, 10_000), equalTo (null)); Thread.sleep (5000); assertThat (nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (500)). execute (), equalTo ("Success")); assertThat (nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (500)). execute (), equalTo ("Success")); assertThat (nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (500)). execute (), equalTo ("Success")); }
public String invokeRemoteService (HystrixCommand.Setter config, int time-out) genereert InterruptedException {String response = null; probeer {response = nieuwe RemoteServiceTestCommand (config, nieuwe RemoteServiceTestSimulator (time-out)). execute (); } catch (HystrixRuntimeException ex) {System.out.println ("ex =" + ex); } antwoord terug; }

In de bovenstaande test hebben we verschillende eigenschappen van de stroomonderbreker ingesteld. De belangrijkste zijn:

  • De CircuitBreakerSleepWindow die is ingesteld op 4.000 ms. Dit configureert het stroomonderbrekingsvenster en definieert het tijdsinterval waarna het verzoek aan de service op afstand zal worden hervat
  • De CircuitBreakerRequestVolumeThreshold die is ingesteld op 1 en definieert het minimum aantal verzoeken dat nodig is voordat het percentage mislukkingen in aanmerking wordt genomen

Met de bovenstaande instellingen op hun plaats, onze HystrixCommand gaat nu open na twee mislukte verzoeken. Het derde verzoek zal niet eens de externe service bereiken, ook al hebben we de servicevertraging ingesteld op 500 ms, Hystrix zal kortsluiten en onze methode zal terugkeren nul als antwoord.

We zullen vervolgens een Draad.sleep (5000) om de limiet van het slaapvenster dat we hebben ingesteld te overschrijden. Dit zal leiden tot Hystrix om het circuit te sluiten en de volgende verzoeken zullen met succes worden verwerkt.

6. Conclusie

Samengevat is Hystrix ontworpen om:

  1. Bied bescherming en controle over storingen en latentie van services die doorgaans via het netwerk worden benaderd
  2. Stop het trapsgewijs optreden van storingen die het gevolg zijn van het uitvallen van sommige services
  3. Faal snel en herstel snel
  4. Degradeer gracieus waar mogelijk
  5. Real-time monitoring en waarschuwing van het commandocentrum bij storingen

In de volgende post zullen we zien hoe we de voordelen van Hystrix kunnen combineren met het Spring-framework.

De volledige projectcode en alle voorbeelden zijn te vinden op het github-project.