Malte Pickhan Zalando Payments GmbH

Wer eine Microservices-Architektur betreibt, sollte sich auch Gedanken über Resilienz machen, um die Verfügbarkeit der eigenen Services zu gewährleisten.

Microservices sind nach wie vor das Thema. Mit einher geht in der Regel die Diskussion über Resilienz. Mit der Integration von Failsafe in Microservices kann man diese resilient gestalten.

Stellen wir uns vor, wir sind Mitarbeiter eines Start-ups, das die Zahlungen für einen Onlineshop abwickelt, der Flip-Flops verkauft. Der Name unseres Start-ups ist Paymento Da wir mit den neuesten Trends gehen, sind alle unsere Dienste als Microservices implementiert. Wenn ein Kunde im Shop einkauft und im Warenkorb den Button JETZT BEZAHLEN drückt, erhält unser System (Payment-Service) eine Anfrage, und wir übernehmen das Risiko für diese Zahlung, sobald wir die Anfrage positiv quittiert haben. Da das Risiko so gering wie möglich gehalten werden soll, rufen wir für jede erhaltene Anfrage einen weiteren Service auf (Solvency-Service), der die Solvenz des Kunden überprüft. Es kommt der Tag der Tage, und unsere Systeme kommen an ihre Grenzen. Da wir unsere Anwendungen in AWS betreiben, können wir entsprechend einfach skalieren. Während einer Lastspitze passiert es dann aber doch: der Solvency Service bricht unter der Last zusammen und ist nicht mehr erreichbar. Wegen eines schlecht konfigurierten Time-outs im Payment-Service laufen alle Anfragen für 30 Sekunden, bis sie fehlschlagen. Es dauert nicht lange, und alle Threads des Payment-Service-Threadpools sind belegt. Sobald neue Instanzen des Solvency-Service starten, werden sie mit Anfragen bombardiert und brechen direkt wieder unter der Last zusammen. Uns bleibt nichts anderes übrig, als sämtlichen eingehenden Verkehr zu blockieren und genügend Instanzen des Solvency-Service zur Verfügung zu stellen. Das bedeutet zumindest kurzzeitig den Totalausfall, und unsere Kunden verlassen frustriert den Flip Flop Summer Sale. Listing 1 zeigt exemplarisch den REST Controller unseres Service, Listing 2 die Clientimplementierung für den Solvency-Service ohne jegliche Absicherung.

@RestController
@Slf4j
@RequestMapping(value = "/payments")
public class PaymentController {
  private PaymentProcessor processor;
  public PaymentController(PaymentProcessor processor) {
    this.processor = processor;
  }
  @PostMapping
  public ResponseEntity createPayment(@RequestBody final PaymentResource paymentResource) {
    log.debug("Processing payment {}", paymentResource);
    boolean processingResult = processor.processPayment(paymentResource);
    if(processingResult) {
      return ResponseEntity.ok(paymentResource);
    } else {
      return ResponseEntity.badRequest().build();
    }
  }
}
public boolean checkSolvency(final ConsumerResource consumerResource) throws URISyntaxException {
  ResponseEntity booleanResponseEntity = restTemplate.postForEntity(new URI(BASE_URL), consumerResource, Boolean.class);
  return booleanResponseEntity.getBody().booleanValue();
}

Was tun, wenn’s brennt? Resilienz!

Um das beschriebene Szenario zu vermeiden, gibt es mehrere Entwurfsmuster, die zum Einsatz kommen können, z. B Circuit Breaker. Hierbei handelt es sich wortwörtlich um Sicherungen, die durchbrennen, wenn ein bestimmter Grenzwert an Anfragen fehlschlägt. Die Sicherung befindet sich dann im geöffneten Zustand. Im geöffneten Zustand schlagen Anfragen an den aufgerufenen Service direkt fehl (Fail-Fast). Zum einen können wir damit die Ressourcen unseres Service schonen, zum anderen wird der aufgerufene Service damit entlastet und kann sich eventuell schneller erholen.

Den vollständigen Artikel lesen Sie in der Ausgabe:

Java Magazin 11.17 - "Per Anhalter durch das Cloud-Universum"

Alle Infos zum Heft
579813086Resiliente Microservices mit Spring Boot und Failsafe
X
- Gib Deinen Standort ein -
- or -