Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fault-Tolerant Clients: Escrevendo Clients e Se...

Rafael Ponte
September 23, 2020

Fault-Tolerant Clients: Escrevendo Clients e Services Tolerantes a Falhas

Com a moda de microservices a industria de software voltou a favorecer o desenvolvimento de sistemas altamente distribuídos, o que possibilitou melhorar a cadência nas entregas dos times e também escalar componentes de maneira independente e isolada. Apesar das vantagens, muitos trade-offs foram colocados na mesa, agora o desenvolvedor precisa lidar com maior complexidade nas integrações e principalmente lidar com os percalços de uma rede instável, com atrasos e falhas parciais.

É justamente essa instabilidade na rede que tira sistemas do ar e, cedo ou tarde, leva a falhas em cascata. Agora, o que era simples não é mais: uma troca de mensagem entre módulos deixou de ser uma chamada de método em memória para se tornar uma jornada lenta e cheia de problemas na rede. Entender a diferença entre escrever código com chamadas locais e remotas é essencial nesse mundo de processos, máquinas e regiões interconectados por protocolos, switches e roteadores na rede.

Por esse motivo, nessa talk, pretendo discutir como nós desenvolvedores podemos escrever sistemas e serviços mais resilientes e tolerantes a falhas, trazendo assim alguma estabilidade dentro desses ambientes distribuídos e instáveis por natureza.

(GRAVAÇÃO: https://youtu.be/TMmN9cR_IsM?list=PLHMMERsvy9EyWQPru4SrJAYHEGKfkjRgP&t=481)

Rafael Ponte

September 23, 2020
Tweet

More Decks by Rafael Ponte

Other Decks in Technology

Transcript

  1. Spring Boot App Spring Boot App Spring Boot App 


    Centralized System Distributed System
  2. Spring Boot App Spring Boot App Spring Boot App 


    Centralized System Distributed System
  3. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  4. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  5. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  6. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  7. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  8. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  9. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  10. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  11. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  12. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  13. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  14. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  15. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  16. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  17. @RestController public class CalculadoraDeFretesController { @GetMapping(path="/fretes/calcula") public ResponseEntity<Frete> calcula(@RequestParam String

    cep) { // consulta frete em outro microsserviço String url = "https://ms.fast-fretes.com/calcula-frete/" ; 
 ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
 return ResponseEntity.ok(frete); } }
  18. ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url

    ) .withParameter("cep", cep ) .execute(); Pode ser qualquer HTTP Client!
  19. ZupClientConfig config = // ... ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) )

    .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  20. ZupClientConfig config = // ... .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>() ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  21. ZupClientConfig config = // ... .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  22. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  23. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  24. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  25. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute(); Depende do caso de uso e contexto
  26. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  27. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .build() ;

    ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  28. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy( )

    .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  29. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom().build() )

    .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  30. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  31. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute(); Transient Failures
  32. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .build() ) .build(); ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  33. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .build() ) .build(); ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute(); Depende do caso de uso e contexto
  34. Spring Boot App Client Spring Boot App Service 
 Internet

    Retry #1: Retry #2: Original Request:
  35. Spring Boot App Client Spring Boot App Service 
 Internet

    Retry #1: Retry #2: Original Request:
  36. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  37. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(Duration.of(100, MILLIS) ) .build() ) .build(); ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  38. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(Duration.of(100, MILLIS) ) .build() ) .build(); ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  39. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(Duration.of(100, MILLIS), 2 ) .build() ) .build(); ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  40. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(Duration.of(100, MILLIS), 2 ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute(); Delay Factor
  41. 100ms
 200ms
 400ms
 800ms
 …
 Retry #1 Retry #2 Retry

    #3 Retry #4 Dobramos Dobramos Dobramos Dobramos
  42. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(Duration.of(100, MILLIS), 2 ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  43. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(Duration.of(100, MILLIS), 2 ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  44. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(Duration.of(100, MILLIS), 2 ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  45. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withExponentialBackoff(Duration.of(100, MILLIS) ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  46. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withExponentialBackoff(Duration.of(100, MILLIS) ) .build() ) .build();
 ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  47. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withExponentialBackoff(Duration.of(100, MILLIS) ) .withJitter(0.25 ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute();
  48. ZupClientConfig config = ZupClientConfig.custom( ) .withRequestTimeout(Duration.of(5, SECONDS) ) .withRetryPolicy(RetryPolicy.custom( )

    .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withExponentialBackoff(Duration.of(100, MILLIS) ) .withJitter(0.25 ) .build() ) .build() ; ZupHttpClient<Frete> client = new ZupHttpClient<>(config) ; Frete frete = client.get(url ) .withParameter("cep", cep ) .execute(); Jitter Factor
  49. 121ms +21ms 
 203ms +3ms 
 393ms -7ms 
 788ms

    -12ms 
 …
 Retry #1 Retry #2 Retry #3 Retry #4
  50. Intervalos longos 
 e ociosos Retry with Exponential Backo f

    Picos ainda podem ocorrer https://aws.amazon.com/blogs/architecture/exponential-backo f f -and-jitter/
  51. Distribui as requisições entre os intervalos Retry with Exponential Backo

    f f and Jitter Menor incidência de picos https://aws.amazon.com/blogs/architecture/exponential-backo f f -and-jitter/
  52. Distribui as requisições entre os intervalos Retry with Exponential Backo

    f f and Jitter Menor incidência de picos https://aws.amazon.com/blogs/architecture/exponential-backo f f -and-jitter/
  53. RetryPolicy.custom( ) .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(ofMillis(100)) .build( ) (Rede

    particionada) https://www.usenix.org/sites/default/ f i les/conference/protected- f i les/srecon18asia_slides_goh.pdf
  54. RetryPolicy.custom( ) .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withDelay(ofMillis(100)) .build( ) (taxa

    de falha de 25 %) https://www.usenix.org/sites/default/ f i les/conference/protected- f i les/srecon18asia_slides_goh.pdf
  55. RetryPolicy.custom( ) .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withExponentialBackoff(ofMillis(100)) .build( ) (taxa

    de falha de 25 %) https://www.usenix.org/sites/default/ f i les/conference/protected- f i les/srecon18asia_slides_goh.pdf
  56. RetryPolicy.custom( ) .retryOn(HttpStatus5xxException.class ) .withMaxAttempts(3 ) .withExponentialBackoff(ofMillis(100)) .withJitter(0.25)
 .build() (taxa

    de falha de 25 %) https://www.usenix.org/sites/default/ f i les/conference/protected- f i les/srecon18asia_slides_goh.pdf
  57. • Sempre de f i na Timeouts • Não faça

    Retry (por default) Pontos de atenção
  58. • Sempre de f i na Timeouts • Não faça

    Retry (por default) • Se f i zer Retry, faça Backo f f (exponential) Pontos de atenção
  59. • Sempre de f i na Timeouts • Não faça

    Retry (por default) • Se f i zer Retry, faça Backo f f (exponential) • Sempre use Jitter Pontos de atenção