Slide 1

Slide 1 text

Проект на java и REACtor Kirill Merkushev а как же тесты?

Slide 2

Slide 2 text

2 Kirill Merkushev Open Source aerokube.com JAVA Go sr. Java Engineer Vivy gmbh BERlin ex. Yandex

Slide 3

Slide 3 text

3 Platform for new age of communication between Patients, Doctors and Insurances

Slide 4

Slide 4 text

4 destroyer monolith as mvp document requestS vaccinations checkups

Slide 5

Slide 5 text

5 Non-scalable Development testing load

Slide 6

Slide 6 text

6 microservices? vaccinations data checkups reminders document request

Slide 7

Slide 7 text

7 communication? how to build

Slide 8

Slide 8 text

8 WE HAVESLA

Slide 9

Slide 9 text

9 communication high level low level

Slide 10

Slide 10 text

10 Predictable extensible reliable e

Slide 11

Slide 11 text

11 architecture

Slide 12

Slide 12 text

12 direct communication?

Slide 13

Slide 13 text

13 event sourcing

Slide 14

Slide 14 text

14 implementation

Slide 15

Slide 15 text

15 Reactor

Slide 16

Slide 16 text

16 service.getUser(id) .filter(permissionsService!::allowed) .flatMap(user !-> service .fetchDocumentRequests(user.getId()) .zipWith(logService!::reportAction) ) .doOnError(logService!::reportFail) .onErrorReturn( RateLimitException.class, status(TOO_MANY_REQUESTS).build() ) .onErrorReturn(status(INTERNAL_SERVER_ERROR).build()); Reactor way

Slide 17

Slide 17 text

17 how to test? how to debug?

Slide 18

Slide 18 text

18 YOU won't WANT to use Reactor

Slide 19

Slide 19 text

19 Until… YOU won't WANT to use Reactor

Slide 20

Slide 20 text

20 PlanWhat we have how to follow use reactor safe

Slide 21

Slide 21 text

21 Reactive manifesto www.reactivemanifesto.org Responsive Resilient Elastic Message Driven

Slide 22

Slide 22 text

22 Non-Blocking With Back-pressure Responsive Resilient Elastic Message Driven Reactive manifesto

Slide 23

Slide 23 text

23 Non-Blocking With Back-pressure Responsive Resilient Elastic Message Driven Reactive manifesto to consume resources only when active

Slide 24

Slide 24 text

24 Reactive Streams

Slide 25

Slide 25 text

25 publisher subscriber subscription Reactive Streams

Slide 26

Slide 26 text

26 Reactive Streams implementation v8 Reactor

Slide 27

Slide 27 text

27 Flux: 0..N mono: 0..1 Reactor

Slide 28

Slide 28 text

28 stream: 0..N optional: 0..1 JDK8 ~

Slide 29

Slide 29 text

29 Reactive Streams rxJava reactor Actors akka fp oop

Slide 30

Slide 30 text

30 service.getUser(id) .filter(permissionsService!::allowed) .flatMap(user !-> service .fetchDocumentRequests(user.getId()) .zipWith(logService!::reportAction) ) .doOnError(logService!::reportFail) .onErrorReturn( RateLimitException.class, status(TOO_MANY_REQUESTS).build() ) .onErrorReturn(status(INTERNAL_SERVER_ERROR).build()); We have

Slide 31

Slide 31 text

31 service.getUser(id) .filter(permissionsService!::allowed) .flatMap(user !-> service .fetchDocumentRequests(user.getId()) .zipWith(logService!::reportAction) ) .doOnError(logService!::reportFail) .onErrorReturn( RateLimitException.class, status(TOO_MANY_REQUESTS).build() ) .onErrorReturn(status(INTERNAL_SERVER_ERROR).build()); We have flow

Slide 32

Slide 32 text

32 service.getUser(id) .filter(permissionsService!::allowed) .flatMap(user !-> service .fetchDocumentRequests(user.getId()) .zipWith(logService!::reportAction) ) .doOnError(logService!::reportFail) .onErrorReturn( RateLimitException.class, status(TOO_MANY_REQUESTS).build() ) .onErrorReturn(status(INTERNAL_SERVER_ERROR).build()); We have merge

Slide 33

Slide 33 text

33 service.getUser(id) .filter(permissionsService!::allowed) .flatMap(user !-> service .fetchDocumentRequests(user.getId()) .zipWith(logService!::reportAction) ) .doOnError(logService!::reportFail) .onErrorReturn( RateLimitException.class, status(TOO_MANY_REQUESTS).build() ) .onErrorReturn(status(INTERNAL_SERVER_ERROR).build()); We have Error handling

Slide 34

Slide 34 text

34 Concurrent HERE Have back-pressure Declarative Have to get used

Slide 35

Slide 35 text

35 Concurrent HERE Have back-pressure Declarative Have to get used Where layers? HOW TO TEST? HOW TO DEBUG?

Slide 36

Slide 36 text

36 how to follow

Slide 37

Slide 37 text

37 Layers service.getUser(id) permissionsService!::allowed logService!::reportFail logService!::reportAction

Slide 38

Slide 38 text

38 Layers service.getUser(id) permissionsService!::allowed logService!::reportFail logService!::reportAction doOnError onErrorReturn Mono Mono

Slide 39

Slide 39 text

39 service.getUser(id) .filter(permissionsService!::allowed) .flatMap(user !-> service .fetchDocumentRequests(user.getId()) .zipWith(logService!::reportAction) ) .doOnError(logService!::reportFail) .onErrorReturn( RateLimitException.class, status(TOO_MANY_REQUESTS).build() ) .onErrorReturn(status(INTERNAL_SERVER_ERROR).build());

Slide 40

Slide 40 text

40 Unhandled exception at reactor.util.Loggers$Slf4JLogger.error(Loggers.java:295) at reactor.core.publisher.SignalLogger.lambda$onErrorCall$5(SignalLogger.java:296) at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onError(FluxPeekFuseable.java:219) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onError(FluxMapFuseable.java:134) at reactor.core.publisher.MonoCollectList$MonoBufferAllSubscriber.onError(MonoCollectList.java:107) at reactor.core.publisher.FluxMergeSequential$MergeSequentialMain.drain(FluxMergeSequential.java:341) at reactor.core.publisher.FluxMergeSequential$MergeSequentialMain.onError(FluxMergeSequential.java:242) at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainAsync(FluxFlattenIterable.java:305) onErrorReturn

Slide 41

Slide 41 text

41 Checkpoints .checkpoint("allow-service") Assembly site of producer [reactor.core.publisher.MonoIgnoreThen] is identified by light checkpoint [allow- service]."description" : "allow-service" at reactor.util.Loggers$Slf4JLogger.error(Loggers.java:295) at reactor.core.publisher.SignalLogger.lambda$onErrorCall$5(SignalLogger.java:296) at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onError(FluxPeekFuseable.java:219) at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onError(FluxMapFuseable.java:134) at reactor.core.publisher.MonoCollectList$MonoBufferAllSubscriber.onError(MonoCollectList.java:107) at reactor.core.publisher.FluxMergeSequential$MergeSequentialMain.drain(FluxMergeSequential.java:341) at reactor.core.publisher.FluxMergeSequential$MergeSequentialMain.onError(FluxMergeSequential.java:242) at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainAsync(FluxFlattenIterable.java:305)

Slide 42

Slide 42 text

42 DEBUG - logs .log("flow") 2018-11-21 17:59:23.285 INFO --- [ Test worker] flow : | request(unbounded) 2018-11-21 17:59:23.275 INFO --- [ Test worker] flow : | onSubscribe([Fuseable] FluxOnAssembly.OnAssemblySubscriber) 2018-11-21 17:59:23.291 ERROR --- [ Test worker] flow : | onError(java.lang.IllegalStateException)

Slide 43

Slide 43 text

43 Use reactor safe

Slide 44

Slide 44 text

44 hooks + tests Hooks.onOperatorDebug(); warn: performance impact Error has been observed by the following operator(s): |_ Mono.create(PublicEmergencyDataRepository.java:101) |_ MonoCreate$DefaultMonoSink.error(DefaultAsyncHandler.java:14) |_ Mono.onErrorMap(PublicEmergencyDataRepository.java:102) |_ Mono.then(PublicEmergencyDataRepository.java:103) |_ MonoCreate$DefaultMonoSink.error(DefaultAsyncHandler.java:14) |_ Mono.onErrorResume(PublicEmergencyDataService.java:70) |_ Mono.then(PublicEmergencyDataService.java:71) |_ Mono.then(UserEventLogConsumer.java:63) |_ Mono.defer(UserEventLogConsumer.java:29)

Slide 45

Slide 45 text

45 unittest void expectTwoUsers(Flux flux) { StepVerifier.create(flux) .expectNextMatches( user !-> user.getUsername().equals("swhite") ) .expectNextMatches( user !-> user.getUsername().equals("jpinkman") ) .expectComplete(); }

Slide 46

Slide 46 text

46 cold/hot Mono.defer Mono.fromCallable Mono.just Mono.fromRunnable Flux.~ Mono.error

Slide 47

Slide 47 text

47 cold/hot .switchIfEmpty(Mono.defer(() !-> service.random(id)) .delayUntil(!__ !-> accessLogService.unsuccess(id, headers)) .map(data !-> ResponseEntity.ok() .body(new ByteArrayResource(data)) ) ) heavY Delay

Slide 48

Slide 48 text

48 cold/hot Mono.just(Files.readString(path)) immediately evaluated

Slide 49

Slide 49 text

49 Retry .retryWhen(it -> it.delayElements(Duration.ofMillis(100)).take(2) ) .defaultIfEmpty(Collections.emptyList())

Slide 50

Slide 50 text

50 Retry .retryWhen(Retry .anyOf(ServerException.class) .retryMax(5) .exponentialBackoff(Duration.ofMillis(100), Duration.ofMillis(500)) ) .onErrorMap(RetryExhaustedException.class, Throwable!::getCause) reactor-extra

Slide 51

Slide 51 text

51 schedulers Mono.fromCallable(() !-> heavyIO(next)) .subscribeOn(Schedulers.parallel()) .log("heavy-io")

Slide 52

Slide 52 text

52 schedulers Mono.fromCallable(() !-> heavyIO(next)) .subscribeOn(Schedulers.parallel()) .log("heavy-io") blocks Flux.interval .delay… .timeout…

Slide 53

Slide 53 text

53 Drawbacks

Slide 54

Slide 54 text

54 benefits only if fully non blocking already reactive

Slide 55

Slide 55 text

55 fully non blocking already reactive or Mono.fromCallable Mono.fromRunnable .subscribeOn( Schedulers.elastic ) Mono!::block Flux…

Slide 56

Slide 56 text

56 fully non blocking already reactive or Mono.fromCallable Mono.fromRunnable .subscribeOn( Schedulers.elastic ) Mono!::block Flux… or deal with leaks, race conditions, deadlocks

Slide 57

Slide 57 text

57 Top 3 checkpoints, logs =========== TAKEAWAYS

Slide 58

Slide 58 text

58 Top 3 checkpoints, logs unit tests, hooks =========== TAKEAWAYS

Slide 59

Slide 59 text

59 Top 3 checkpoints, logs retries unit tests, hooks =========== TAKEAWAYS

Slide 60

Slide 60 text

60 I am open to talk! github.com/lanwen t.me/lanwen kirill merkushev @delnariel