TIBCO BWCE and Netflix' Hystrix Circuit Breaker for Cloud Native Middleware Microservices
Hands-on Hystrix - Best Practices und Stolperfallen
-
Upload
inovex-gmbh -
Category
Software
-
view
569 -
download
0
Transcript of Hands-on Hystrix - Best Practices und Stolperfallen
WER ERZÄHLT DIE NÄCHSTEN 50+ MINUTENETWAS?
Gerrit Brehmer, Karlsruhe
Auf der Arbeit: So�ware-Entwickler/Architekt bei der inovex GmbH
In der Freizeit: Meine Tochter & Smart Home
WARUM IST ROBUSTHEIT WICHTIG?Michael Nygard über den Alltag
bei mittleren bis großen SW-Systemen:
“The normal mode of operation is partial failure.“
VORAB: HÄUFIGES (ANTI-)PATTERN@Transactional @RequestMapping("api") public ResponseEntity updateUser(UserData data) { validate(data); // DB
checkWithExternalService(data); // Extern
Entity updatedUsed = saveToDb(data); // DB
return toResponseEntity(updatedUsed); // DB / DTO }
DB-Transaktion/Verbindung so kurz wie möglichWartende Requests blockieren kostbare RessourcenBöse: OpenSessionInViewFilter
ABHÄNGIGKEITEN IM GRIFFNichtfunktionale Anforderungen
an den eigenen Dienstan und von externen Diensten
SLAs (leider viel zu selten vorhanden)Entkopplung & Isolation
Ziele:
Eine robuste und fehlertolerante AnwendungDie beste Fehlerbehandlung ist die, von der der Nutzer nichts mitbekommt
ENTKOPPLUNG - USE CASE #1Berechnung von kundenspezifischen Empfehlungen durch ein entferntes
System
enge Integration in einen Großteil der SeitenFehler/Timeouts schlagen sofort auf diese Seiten durch
Lösung mit Hystrix: Alternative Empfehlungen auf Basis von statischen Regeln/einfachen
Filtern (kein entferntes System) im Fehlerfall
ERGEBNIS OHNE HYSTRIX@Inject RecoService recoClient;
public Movies getReco(long customerId) { return recoClient.getMovies(customerId); }
ERGEBNIS MIT HYSTRIXSTATISCHER FALLBACK
public class RecoForCustomerCommand extends HystrixCommand<Movies> { ... public RecoForCustomerCommand(RecoService recoClient, long customerId) { super( Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("Reco")) .andCommandKey(HystrixCommandKey.Factory.asKey("RecoForCustomer")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RecoTP"))); this.recoClient = recoClient; this.customerId = customerId; } @Override protected Movies run() { return recoClient.getMovies(customerId); } @Override protected Movies getFallback() { return Movies.EMPTY_LIST; } }
public Movies getReco(long customerId) { return new RecoForCustomerCommand(recoClient, customerId).execute(); }
ERGEBNIS MIT HYSTRIXDYNAMISCHER FALLBACK
public class RecoForCustomerCommand extends HystrixCommand<Movies> { ... public RecoForCustomerCommand(RecoService recoClient, long custId) { super( Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("Reco")) .andCommandKey(HystrixCommandKey.Factory.asKey("RecoForCustomer")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RecoTP"))); this.recoClient = recoClient; this.customerId = custId; } @Override protected Movies run() { return recoClient.getMovies(customerId); } @Override protected Movies getFallback() { return InternalRecoClient.getTopMovies(); } }
public Movies getReco(long customerId) { return new RecoForCustomerCommand(recoService, customerId).execute(); }
ENTKOPPLUNG - USE CASE #2Fehler-Kaskaden: Laufzeit-Probleme wirken sich auch auf aufrufende
Systeme aus
begrenzte Ressourcenwerden gebundenAufwand für Connection-Handling steigt rapideSeiteneffekte in andereSysteme
ENTKOPPLUNG - USE CASE #2Lösung mit Hystrix:
Direkte FehlerrückgabeVerhindert weitereLaufzeitprobleme
HYSTRIX PATTERNSImplementierung diverser Resilience Patterns
Graceful degradation (UseCase 1)Fail Fast (Use case 2)Fail silentBulkheadingCircuit Breaker
THREAD-POOLS ODER SEMAPHORENThread Pools
können zwischen mehreren Commands geshared werdenbessere Isolierungspürbarer Overhead (0-9ms, ø 1ms)Thread-Pools verhindern nicht, dass Aufrufe dauerha� blockieren
Java Threads können nicht gesichert beendet werdenKomplexer in der Implementierung
Timeouts setzen - immer und überallNicht blockierende APIs verwenden (Java NIO Channel, Netty)Thread.currentThread().isInterrupted() nutzen
THREAD-POOLS ODER SEMAPHORENSemaphoren
minimaler OverheadTimeouts ab Hystrix 1.4 ... Aber:
Ausführungsdauer wird nicht verringert (Caller-Thread)Fallback wird in einem extra Thread ausgeführt, wenn Timeouterreicht wirdFallback-Rückgabe erst mit Abschluss Caller-Thread
Empfehlung für Aufrufe mit minimaler Latenz
EINSCHRÄNKUNGEN DURCH THREADPOOLSThreadLocals stehen nicht zur Verfügung
MDC / NDC Log Kontexte(DB-)TransaktionsstatusAktueller HTTP Request / Security ContextThread gebundene DI-Scopes (RequestScope, ThreadScope)
LÖSUNG THREADLOCALCustom ConcurrencyStrategy
public class CustomStrategy extends HystrixConcurrencyStrategy { @Override public <T> Callable<T> wrapCallable(final Callable<T> callable) { final Map context = MDC.getContext(); final RequestAttributes attr = RequestContextHolder.get(); return super.wrapCallable(new Callable<T>() { @Override public T call() throws Exception { try { MDC.replaceAll(context); RequestContextHolder.set(attr); return callable.call(); } finally { RequestContextHolder.resetRequestAttributes(); MDC.clear(); } ...
HystrixPlugins.getInstance().registerConcurrencyStrategy(new CustomStrategy());
EINSATZ IN SERVLET-/JEE CONTAINERNDynamisches Deployment statt Container RestartPotentielle Resource Leaks
Lösung:
Hystrix.reset()Servlet listenerShutdown HookSingelton @PreDestroy-Methode
EXCEPTIONHANDLINGHystrix Standard: Unchecked Exceptions
Eigene werden in HystrixRuntimeExceptions gewrapptKompatibilität Legacy Code
Neue FehlerfälleTimeout durch HystrixOffener Circuit, Pool ausgelastet
Sonderfall HystrixBadRequestExceptionStellt fehlerha�e Anfrage darKein FallbackKeine Fehler-Statistik
EXCEPTIONS: ABWÄRTSKOMPATIBILITÄTpublic abstract class LegacyCommand extends HystrixCommand { ... public T executeWithCheckedEx() throws CheckedIOException { try { return super.execute(); } catch (RuntimeException e) { if (isCircuitBreakerOpen() || isResponseRejected() || isResponseTimedOut() || isResponseShortCircuited()) { throw new CheckedIOException("req. aborted:"+e.getMessage()); } else if (e.getCause() instanceof CheckedIOException) { throw (CheckedIOException) e.getCause(); } else { throw e; } } }
HYSTRIX-ANNOTATIONS: JAVANICAAspekt zur Erstellung von HystrixCommands zur Laufzeit
Interceptoren & andere Aspekte greifen nicht!Konfiguration per Annotation
Einschränkung zur Laufzeit & WiederverwendbarkeitImplizite Regeln (Convention over Configuration)
Exception-Handling andersExceptiontypen können ignoriert werden und werden ohne"Verpackung" zurückgeworfenExceptionhandling ansonsten identischÄnderung Exception Handling durch AnpassungHystrixCommandAspect
HYSTRIX/(NETFLIX)-INTEGRATION MITSPRING
Spring Cloud NetflixFeatures per Annotation in der JavaConfig aktivieren
Javanica Aktivierung & ShutdownHookHystrix Event StreamHystrix DashboardHystrix Turbine (auf Cloud Anwendung spezialisiert)
Integration mit weiteren Netflix Bibliotheken (Heureka, Zuul,Ribbon,..)
MICROSERVICES & HYSTRIXNetflix einer der großen Treiber der Microservice ArchitekturHystrix wird hauptsächlich in deren API Gateways eingesetzt
Reduzierung der RequestsHandling vieler AbhängigkeitenParallele Verarbeitung (RxJava)Weitere Optimierungen:
RequestCollapserRequestCache
DEMOJHipster Stack als API Gateway
Angular JS / BootstrapSpring BootHystrix
Drei simple REST Microservices(Spring Boot)Monitoring
Hystrix DashboardKibana Dashboard (ELK)
FAZIT & AUSBLICKHystrix
Entkopplung und Isolierung leicht gemachtNeue Version bringt weitere Features und VerbesserungenDennoch: Komplexität mit Hystrix ist nicht zu unterschätzen
Hystrix für MicroservicesBei Inter-Service KommunikationIntegration externer DiensteAPI Gateway
BILDERVERZEICHNISHystrix Patterns:https://www.flickr.com/photos/ramnaganat/7154180752Circuit Breaker: http://martinfowler.com/bliki/CircuitBreaker.htmlPitfalls: https://www.flickr.com/photos/nafmo/1488330724