Ausfälle im Betrieb kennt jeder – und dennoch ist man selten ausreichend darauf vorbereitet. Allerdings könnten viele dieser Ausfälle deutlich abgemildert oder gar gänzlich verhindert werden. Netflix hat das Problem erkannt und die Bibliothek Hystrix entwickelt, die Entwickler bei der Implementierung von Resilience Patterns wie „Fail Fast“ und „Graceful Degradation“ unterstützt. In einer Microservice-Architektur vergleichbar mit Netflix kann die Anzahl von abhängigen Services nochmal deutlich ansteigen und Hystrix in einem solchen Szenario seine Stärken ausspielen. Überraschungen kann es aber auch bei Hystrix geben. Um diesen entgegenzuwirken bietet dieser Talk Erfahrungen und Beispiele aus bereits angepassten Anwendungen für die Integration von Hystrix. Eine Demo mit den kleinen Diensten zeigt, wie kleine Ursachen große Ausfälle auslösen können. Mit Hystrix im Einsatz dagegen werden die Folgen durch automatische Behandlung der Fehler minimiert.
Event: JAX 2016, 19.04.2016
Speaker: Gerrit Brehmer
Mehr Tech-Voträge: https://www.inovex.de/de/content-pool/vortraege/
Infrastructure as (real) Code – Manage your K8s resources with Pulumi
Hands-on Hystrix - Best Practices und Stolperfallen
2. WER ERZÄHLT DIE NÄCHSTEN 50+ MINUTEN
ETWAS?
Gerrit Brehmer, Karlsruhe
Auf der Arbeit: So ware-Entwickler/Architekt bei der inovex GmbH
In der Freizeit: Meine Tochter & Smart Home
4. WARUM IST ROBUSTHEIT WICHTIG?
Michael Nygard über den Alltag
bei mittleren bis großen SW-Systemen:
“The normal mode of operation
is partial failure.“
8. ABHÄNGIGKEITEN IM GRIFF
Nichtfunktionale Anforderungen
an den eigenen Dienst
an und von externen Diensten
SLAs (leider viel zu selten vorhanden)
Entkopplung & Isolation
Ziele:
Eine robuste und fehlertolerante Anwendung
Die beste Fehlerbehandlung ist die,
von der der Nutzer nichts mitbekommt
9. ENTKOPPLUNG - USE CASE #1
Berechnung von kundenspezifischen Empfehlungen durch ein entferntes
System
enge Integration in einen Großteil der Seiten
Fehler/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
11. ERGEBNIS MIT HYSTRIX
STATISCHER 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();
}
12. ERGEBNIS MIT HYSTRIX
DYNAMISCHER 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();
}
13. ENTKOPPLUNG - USE CASE #2
Fehler-Kaskaden: Laufzeit-Probleme wirken sich auch auf aufrufende
Systeme aus
begrenzte Ressourcen
werden gebunden
Aufwand für Connection-
Handling steigt rapide
Seiteneffekte in andere
Systeme
14. ENTKOPPLUNG - USE CASE #2
Lösung mit Hystrix:
Direkte Fehlerrückgabe
Verhindert weitere
Laufzeitprobleme
18. THREAD-POOLS ODER SEMAPHOREN
Thread Pools
können zwischen mehreren Commands geshared werden
bessere Isolierung
spürbarer Overhead (0-9ms, ø 1ms)
Thread-Pools verhindern nicht, dass Aufrufe dauerha blockieren
Java Threads können nicht gesichert beendet werden
Komplexer in der Implementierung
Timeouts setzen - immer und überall
Nicht blockierende APIs verwenden (Java NIO Channel, Netty)
Thread.currentThread().isInterrupted() nutzen
19. THREAD-POOLS ODER SEMAPHOREN
Semaphoren
minimaler Overhead
Timeouts ab Hystrix 1.4 ... Aber:
Ausführungsdauer wird nicht verringert (Caller-Thread)
Fallback wird in einem extra Thread ausgeführt, wenn Timeout
erreicht wird
Fallback-Rückgabe erst mit Abschluss Caller-Thread
Empfehlung für Aufrufe mit minimaler Latenz
20. EINSCHRÄNKUNGEN DURCH THREADPOOLS
ThreadLocals stehen nicht zur Verfügung
MDC / NDC Log Kontexte
(DB-)Transaktionsstatus
Aktueller HTTP Request / Security Context
Thread gebundene DI-Scopes (RequestScope, ThreadScope)
21. LÖSUNG THREADLOCAL
Custom 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());
23. EXCEPTIONHANDLING
Hystrix Standard: Unchecked Exceptions
Eigene werden in HystrixRuntimeExceptions gewrappt
Kompatibilität Legacy Code
Neue Fehlerfälle
Timeout durch Hystrix
Offener Circuit, Pool ausgelastet
Sonderfall HystrixBadRequestException
Stellt fehlerha e Anfrage dar
Kein Fallback
Keine Fehler-Statistik
24. EXCEPTIONS: ABWÄRTSKOMPATIBILITÄT
public 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;
}
}
}
25. HYSTRIX-ANNOTATIONS: JAVANICA
Aspekt zur Erstellung von HystrixCommands zur Laufzeit
Interceptoren & andere Aspekte greifen nicht!
Konfiguration per Annotation
Einschränkung zur Laufzeit & Wiederverwendbarkeit
Implizite Regeln (Convention over Configuration)
Exception-Handling anders
Exceptiontypen können ignoriert werden und werden ohne
"Verpackung" zurückgeworfen
Exceptionhandling ansonsten identisch
Änderung Exception Handling durch Anpassung
HystrixCommandAspect
26. HYSTRIX/(NETFLIX)-INTEGRATION MIT
SPRING
Spring Cloud Netflix
Features per Annotation in der JavaConfig aktivieren
Javanica Aktivierung & ShutdownHook
Hystrix Event Stream
Hystrix Dashboard
Hystrix Turbine (auf Cloud Anwendung spezialisiert)
Integration mit weiteren Netflix Bibliotheken (Heureka, Zuul,
Ribbon,..)
27. MICROSERVICES & HYSTRIX
Netflix einer der großen Treiber der Microservice Architektur
Hystrix wird hauptsächlich in deren API Gateways eingesetzt
Reduzierung der Requests
Handling vieler Abhängigkeiten
Parallele Verarbeitung (RxJava)
Weitere Optimierungen:
RequestCollapser
RequestCache
28. DEMO
JHipster Stack als API Gateway
Angular JS / Bootstrap
Spring Boot
Hystrix
Drei simple REST Microservices
(Spring Boot)
Monitoring
Hystrix Dashboard
Kibana Dashboard (ELK)
29. FAZIT & AUSBLICK
Hystrix
Entkopplung und Isolierung leicht gemacht
Neue Version bringt weitere Features und Verbesserungen
Dennoch: Komplexität mit Hystrix ist nicht zu unterschätzen
Hystrix für Microservices
Bei Inter-Service Kommunikation
Integration externer Dienste
API Gateway