Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Reactive Fault Tolerant Programming with Hystrix and RxJava

4.528 Aufrufe

Veröffentlicht am

As presented at ArchConf 2016 in San Diego, CA.

Veröffentlicht in: Software

Reactive Fault Tolerant Programming with Hystrix and RxJava

  1. 1. REACTIVE FAULT TOLERANT PROGRAMMING with Hystrix and RxJava Matt Stine (@mstine)
  2. 2. Matt StineSenior Product Manager - Pivotal Software, Inc. Author of: http://bit.ly/cloud-native-book
  3. 3. © Copyright 2015 Pivotal. All rights reserved. 3 MICROSERVICES!!!!!
  4. 4. Typical Microservices Architecture ÂAPI ÂAPI ! ! ! µS µS µS µS µS
  5. 5. Trace a Request ÂAPI ÂAPI ! ! ! µS µS µS µS µS
  6. 6. Consider this subgraph… ÂAPI µS µS
  7. 7. public ResponseEntity<Foo> handleIncomingRequest() { Foo foo = new Foo(); foo.addPart(serviceOne.getContributionToFoo()); foo.addPart(serviceTwo.getContributionToFoo()); return new ResponseEntity<>(foo, HttpStatus.OK); } Block and wait!
  8. 8. Meanwhile in Service Two… ! ! µS µS µS
  9. 9. public ResponseEntity<FooPart> handleIncomingRequest() { FooPart fooPart = new FooPart(); fooPart.addSubPart(serviceThree.getContributionToFooPart()); fooPart.addSubPart(serviceFour.getContributionToFooPart()); return new ResponseEntity<>(fooPart, HttpStatus.OK); } Block and wait!
  10. 10. BUT DAT LATENCY THO
  11. 11. Futures! ExecutorService executor = createExecutorService(); public ResponseEntity<Foo> handleIncomingRequest() { Foo foo = new Foo(); Future<FooPart> partOne = executor.submit(new CallToServiceOne()); Future<FooPart> partTwo = executor.submit(new CallToServiceTwo()); foo.addPart(partOne.get()); foo.addPart(partTwo.get()); return new ResponseEntity<>(foo, HttpStatus.OK); }
  12. 12. The service graph changes… public ResponseEntity<Foo> handleIncomingRequest() { Foo foo = new Foo(); Future<FooPart> partOne = executor.submit(new CallToServiceOne()); Future<FooPart> partTwo = executor.submit( new CallToServiceTwo(partOne.get())); Future<FooPart> partThree = executor.submit(new CallToServiceThree()) foo.addPart(partTwo.get()); foo.addPart(partThree.get()); return new ResponseEntity<>(foo, HttpStatus.OK); } Block and wait! Blocked until two completes!
  13. 13. CompletableFutures public ResponseEntity<Foo> handleIncomingRequest() { Foo foo = new Foo(); CompletableFuture<FooPart> partTwo = CompletableFuture.supplyAsync( new CallToServiceOne()) .thenApplyAsync(new CallToServiceTwo()); CompletableFuture<FooPart> partThree = CompletableFuture.supplyAsync( new CallToServiceThree()); foo.addPart(partTwo.get()); foo.addPart(partThree.get()); return new ResponseEntity<>(foo, HttpStatus.OK); }
  14. 14. As composition becomes more complex, CompletableFuture API is lacking…
  15. 15. An API like this would be nice… List<Integer> transactionsIds = transactions.stream() .filter(t -> t.getType() == Transaction.GROCERY) .sorted(comparing(Transaction::getValue).reversed()) .map(Transaction::getId) .collect(toList());
  16. 16. Hold that thought…
  17. 17. RESPONSIVE
  18. 18. RESILIENT
  19. 19. ELASTIC
  20. 20. MESSAGE-DRIVEN
  21. 21. RxJava http://reactivex.io https://github.com/ReactiveX/RxJava
  22. 22. Observer Pattern
  23. 23. Hello Observable Observable<Integer> observableString = Observable.create( new Observable.OnSubscribe<Integer>() { public void call(Subscriber<? super Integer> subscriber) { for (int i = 0; i < 5; i++) { subscriber.onNext(i); } subscriber.onCompleted(); } }); Emit an item! I’m done emitting items!
  24. 24. Observable<Integer> observableString = Observable.create( subscriber -> { for (int i = 0; i < 5; i++) { subscriber.onNext(i); } subscriber.onCompleted(); }); Hello Observable (JDK 8)
  25. 25. Hello Subscription Subscription subscription = observableString.subscribe( new Observer<Integer>() { public void onCompleted() { System.out.println("Observable completed"); } public void onError(Throwable throwable) { System.out.println("Oh noes! Something wrong happened!"); } public void onNext(Integer integer) { System.out.println("Item is " + integer); } });
  26. 26. Hello Subscription (JDK 8) Subscription subscription = observableString.subscribe( item -> System.out.println("Lambda says: " + item), throwable -> System.out.println("Lambda says: Oh noes! Something wrong happened!"), () -> System.out.println("Lambda says: Observable completed"));
  27. 27. All together now… Observable.create( subscriber -> { for (int i = 0; i < 5; i++) { subscriber.onNext(i); } subscriber.onCompleted(); }).subscribe( item -> System.out.println("Lambda says: " + item), throwable -> System.out.println("Lambda says: Oh noes! Something wrong happened!"), () -> System.out.println("Lambda says: Observable completed"));
  28. 28. Back to Our Service Graph ÂAPI ÂAPI ! ! ! µS µS µS µS µS
  29. 29. With Callables Observable.fromCallable(new CallToServiceOne()) .flatMap(serviceOneResult -> Observable.fromCallable( new CallToServiceTwo(serviceOneResult))) .zipWith(Observable.fromCallable( new CallToServiceThree()), (FooPart resultFromServiceTwo, FooPart resultFromServiceThree) -> { Foo foo = new Foo(); foo.addPart(resultFromServiceTwo); foo.addPart(resultFromServiceThree); return foo; }).subscribe(System.out::println);
  30. 30. With Lambdas Observable.<FooPart>create(serviceOneSubscriber -> { serviceOneSubscriber.onNext(new FooPart("one")); serviceOneSubscriber.onCompleted(); }).flatMap(serviceOneResult -> Observable.<FooPart>create(serviceTwoSubscriber -> { serviceTwoSubscriber.onNext(new FooPart(serviceOneResult + " + two")); serviceTwoSubscriber.onCompleted(); })).zipWith(Observable.<FooPart>create(serviceThreeSubscriber -> { serviceThreeSubscriber.onNext(new FooPart("three")); serviceThreeSubscriber.onCompleted(); }), (resultFromServiceTwo, resultFromServiceThree) -> { Foo foo = new Foo(); foo.addPart(resultFromServiceTwo); foo.addPart(resultFromServiceThree); return foo; }).subscribe(System.out::println);
  31. 31. RxJava Operator Tour
  32. 32. Combining Observables • merge() - combine multiple Observables so they act like a single Observable • zip() - combine sets of items from multiple Observables via a Function, and emit the results https://github.com/ReactiveX/RxJava/wiki/Combining-Observables
  33. 33. Conditionals • doWhile() - emit Observable sequence, then repeat the sequence as long as the condition remains true • ifThen() - only emit Observable sequence if a condition is true, otherwise emit empty or default sequence • skipUntil() - discard emitted items from Observable until a second Observable emits an item, then emit the remainder • takeUntil() - emit items from Observable until a second Observable emits or notifies https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators
  34. 34. Boolean Operators • all() - do all the emitted items meet some criteria? • contains() - does the Observable emit a particular item? • exists() / isEmpty() - does an Observable emit items or not? • sequenceEqual() - do two Observables emit equal sequences? https://github.com/ReactiveX/RxJava/wiki/Conditional-and-Boolean-Operators
  35. 35. Filtering • filter() - filter Observable with a predicate • take(n) - emit only the first n items • skip(n) - ignore the first n items emitted • sample() - sample items according to a periodic interval https://github.com/ReactiveX/RxJava/wiki/Filtering-Observables
  36. 36. Transforming • map() - apply a function to each emitted item • flatMap() - transform items emitted by Observable into Observables, then flatten • scan() - apply a function to each emitted item, then feedback result and repeat • buffer() - gather emitted items into bundles and emit the bundles https://github.com/ReactiveX/RxJava/wiki/Transforming-Observables
  37. 37. Backpressure!
  38. 38. – https://github.com/ReactiveX/RxJava/wiki/Backpressure “…a quickly-producing Observable meets a slow-consuming observer.”
  39. 39. Backpressure Observable Observable.create(subscriber -> IntStream.iterate(0, i -> i + 2) .forEach(value -> subscriber.onNext(value))) .onBackpressureBuffer() .subscribe(new BackpressureSubscriber());
  40. 40. Backpressure Subscriber public class BackpressureSubscriber extends Subscriber<Object> { private int counter = 0; @Override public void onStart() { request(10); } @Override public void onNext(Object o) { if (counter < 9) { processItem(o); } else { processItem(o); resetCounter(); request(10); } } Please only give me this many! OK, I can handle more now.
  41. 41. Backpressure Subscriber private void processItem(Object o) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } counter++; System.out.println(counter + " : " + o); } private void resetCounter() { counter = 0; System.out.println("FETCH MORE"); } Simulate Processing Latency }
  42. 42. WHAT ABOUT FAILURES?
  43. 43. What happens if we cut here? ÂAPI ÂAPI ! ! ! µS µS µS µS µS
  44. 44. Or here? ÂAPI ÂAPI ! ! ! µS µS µS µS µS
  45. 45. Or here? ÂAPI ÂAPI ! ! ! µS µS µS µS µS
  46. 46. 500 INTERNAL SERVER ERROR
  47. 47. Fault Tolerance at Netflix http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html
  48. 48. Without taking steps to ensure fault tolerance, 30 dependencies each with 99.99% uptime would result in 2+ hours downtime/ month (99.99%30 = 99.7% uptime = 2.16 hours/month). http://techblog.netflix.com/2012/02/fault-tolerance-in-high-volume.html
  49. 49. The Circuit Breaker Pattern
  50. 50. Circuit Breaker State Machine Closed on call / pass through call succeeds / reset count call fails / count failure threshold reached / trip breaker Open on call / fail on timeout / attempt reset Half-Open on call / pass through call succeeds / reset call fails / trip breaker trip breaker trip breaker attempt reset reset
  51. 51. https://github.com/Netflix/Hystrix
  52. 52. Hello Hystrix World public class CommandHelloWorld extends HystrixCommand<String> { private final String name; public CommandHelloWorld(String name) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.name = name; } @Override protected String run() { // Do something that might fail... return "Hello " + name + "!"; } }
  53. 53. Synchronous String s = new CommandHelloWorld("Bob").execute(); Block and wait!
  54. 54. Asynchronous Future<String> s = new CommandHelloWorld("Bob").queue();
  55. 55. Reactive! Observable<String> s = new CommandHelloWorld("Bob").observe(); s.subscribe(val -> { // value emitted here });
  56. 56. Fail Fast public class CommandThatFailsFast extends HystrixCommand<String> { private final boolean throwException; public CommandThatFailsFast(boolean throwException) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.throwException = throwException; } @Override protected String run() { if (throwException) { throw new RuntimeException("failure from CommandThatFailsFast"); } else { return "success"; } } }
  57. 57. Fail Silently public class CommandThatFailsSilently extends HystrixCommand<String> { private final boolean throwException; public CommandThatFailsSilently(boolean throwException) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.throwException = throwException; } @Override protected String run() { if (throwException) { throw new RuntimeException("failure from CommandThatFailsSilently"); } else { return "success"; } } @Override protected String getFallback() { return null; } } Fallback behavior! }
  58. 58. Static Fallback @Override protected String run() { if (throwException) { throw new RuntimeException("failure from CommandWithStaticFallback"); } else { return "success"; } } @Override protected String getFallback() { return "fallback"; } Fallback behavior! }
  59. 59. Stubbed Fallback @Override protected UserAccount run() { // fetch UserAccount from remote service // return UserAccountClient.getAccount(customerId); throw new RuntimeException("forcing failure for example"); } @Override protected UserAccount getFallback() { /** * Return stubbed fallback with some static defaults, placeholders, * and an injected value 'countryCodeFromGeoLookup' that we'll use * instead of what we would have retrieved from the remote service. */ return new UserAccount(customerId, "Unknown Name", countryCodeFromGeoLookup, true, true, false); } Fallback behavior! }
  60. 60. Fallback: Cache via Network
  61. 61. Primary + Secondary with Fallback
  62. 62. How Hystrix Works!
  63. 63. Hystrix Dashboard
  64. 64. A Failing Circuit
  65. 65. RxJava + Hystrix
  66. 66. With Callables Observable.fromCallable(new CallToServiceOne()) .flatMap(serviceOneResult -> Observable.fromCallable( new CallToServiceTwo(serviceOneResult))) .zipWith(Observable.fromCallable( new CallToServiceThree()), (FooPart resultFromServiceTwo, FooPart resultFromServiceThree) -> { Foo foo = new Foo(); foo.addPart(resultFromServiceTwo); foo.addPart(resultFromServiceThree); return foo; }).subscribe(System.out::println);
  67. 67. With HystrixObservableCommands new CallToServiceOneCommand("callToServiceOne").observe() .flatMap(serviceOneResult -> new CallToServiceTwoCommand("callToServiceTwo", serviceOneResult).observe()) .zipWith(new CallToServiceThreeCommand("callToServiceThree").observe(), (FooPart resultFromServiceTwo, FooPart resultFromServiceThree) -> { Foo foo = new Foo(); foo.addPart(resultFromServiceTwo); foo.addPart(resultFromServiceThree); return foo; }).subscribe(System.out::println);
  68. 68. Call to Service One public class CallToServiceOneCommand extends HystrixObservableCommand<FooPart> { public CallToServiceOneCommand(String name) { super(HystrixCommandGroupKey.Factory.asKey(name)); } @Override protected Observable<FooPart> construct() { return Observable.create(new Observable.OnSubscribe<FooPart>() { @Override public void call(Subscriber<? super FooPart> subscriber) { subscriber.onNext(new FooPart("one")); subscriber.onCompleted(); } }); } }
  69. 69. public class CallToServiceTwoCommand extends HystrixObservableCommand<FooPart> { private final FooPart dependencyFromServiceOne; private CallToServiceTwoCommand(String name, FooPart dependencyFromServiceOne) { super(HystrixCommandGroupKey.Factory.asKey(name)); this.dependencyFromServiceOne = dependencyFromServiceOne; } @Override protected Observable<FooPart> construct() { return Observable.create(new Observable.OnSubscribe<FooPart>() { @Override public void call(Subscriber<? super FooPart> subscriber) { subscriber.onNext(new FooPart(dependencyFromServiceOne + " + two")); subscriber.onCompleted(); } }); } } Call to Service Two
  70. 70. Spring Cloud http://cloud.spring.io
  71. 71. Hystrix with Spring Cloud @SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
  72. 72. Hystrix with Spring Cloud @Service @EnableConfigurationProperties(FortuneProperties.class) public class FortuneService { @Autowired FortuneProperties fortuneProperties; @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "fallbackFortune") public Fortune randomFortune() { return restTemplate.getForObject("http://fortunes/random", Fortune.class); } private Fortune fallbackFortune() { return new Fortune(42L, fortuneProperties.getFallbackFortune()); } } }
  73. 73. RxJava + Hystrix + Spring Cloud @HystrixCommand(fallbackMethod = "stubMovie") public Observable<Movie> getMovie(final String mlId) { return new ObservableResult<Movie>() { @Override public Movie invoke() { return restTemplate.getForObject( "http://springbox-catalog/movies/{mlId}", Movie.class, mlId); } }; } private Movie stubMovie(final String mlId) { Movie stub = new Movie(); stub.setMlId(mlId); stub.setTitle("Interesting...the wrong title. Sssshhhh!"); return stub; }
  74. 74. RxJava + Hystrix + Spring Cloud @RequestMapping("/movie/{mlId}") public Observable<MovieDetails> movieDetails(@PathVariable String mlId) { return Observable.zip( catalogIntegrationService.getMovie(mlId), reviewsIntegrationService.reviewsFor(mlId), recommendationsIntegrationService.getRecommendations(mlId), (movie, reviews, recommendations) -> { MovieDetails movieDetails = new MovieDetails(); movieDetails.setMlId(movie.getMlId()); movieDetails.setTitle(movie.getTitle()); movieDetails.setReviews(reviews); movieDetails.setRecommendations(recommendations); return movieDetails; } ); }
  75. 75. Reactive Landscape
  76. 76. Reactive Streams http://www.reactive-streams.org/
  77. 77. Project Reactor https://projectreactor.io/
  78. 78. JDK 9 Flow API http://download.java.net/jdk9/docs/api/java/util/concurrent/Flow.html
  79. 79. REACTIVE FAULT TOLERANT PROGRAMMING with Hystrix and RxJava Get your FREE eBook! http://bit.ly/cloud-native-book Matt StineSenior Product Manager - Pivotal Software, Inc.
 @mstine
 matt.stine@gmail.com
 http://mattstine.com

×