+ All Categories
Home > Technology > Designing, Implementing, and Using Reactive APIs

Designing, Implementing, and Using Reactive APIs

Date post: 16-Apr-2017
Category:
Upload: spring-by-pivotal
View: 1,501 times
Download: 0 times
Share this document with a friend
54
Unless otherwise indicated, these slides are © 2013-2016 Pivotal Software, Inc. and licensed under a Creative Commons Attribution-NonCommercial license: http://creativecommons.org/licenses/by-nc/3.0/ Designing, Implementing, and Using Reactive APIs Ben Hale (@nebhale) Paul Harris (@twoseat)
Transcript
Page 1: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Designing, Implementing, and Using Reactive APIs

Ben Hale (@nebhale)Paul Harris (@twoseat)

Page 2: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Introduction to Reactive Programming

• Reactive is used to broadly define event-driven systems

• Moves imperative logic to • asynchronous • non-blocking • functional-style code

• Allows stable, scalable access to external systems

2

Page 3: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Cloud Foundry Java Client

• Cloud Foundry is composed of • RESTful APIs • Secured by OAuth • Transmitting large JSON request and response

payloads

• APIs are low level, often requiring orchestration

3

Page 4: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Cloud Foundry Java Client

• Java has become the preferred language for orchestrating Cloud Foundry

• Complex orchestration is difficult when working against generic payloads

• A strongly typed Java API is required for maintainability

4

Page 5: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Generic Request and Response Payloads

5

Map<String, Object> request = new HashMap<>();request.put("name", "test-application"); request.put("application", Paths.get("target/test-application.jar"));request.put("buildpack", "java-buildpack");

this.operations.applications() .create(request) .map(response -> response.get("id"));

Page 6: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Strongly Typed Request and Response Payloads

6

this.operations.applications() .create(CreateApplicationRequest.builder() .name("test-application") .application(Paths.get("target/test-application.jar")) .buildpack("java-buildpack") .build()) .map(CreateApplicationResponse::getId);

Page 7: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Cloud Foundry Java Client

• The high-latency network interaction is a good fit for a Reactive Design

• Encourages asynchronous and highly concurrent usage

• Partnering with Project Reactor early influenced both the design of the client and of Reactor

7

Page 8: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Design

Page 9: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

When To Use Reactive

• Networking • Latency • Failures • Backpressure

• Highly Concurrent Operations • Scalability

9

Page 10: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

What Should a Reactive API Return?

• Flux<T> • Returns 0 to N values

• Mono<T> • Returns 0 to 1 value

• The Java Client often returns Monos containing collections

10

Page 11: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Using a Flux<T> Response

11

Flux<Application> listApplications() {...}Flux<String> listApplicationNames() { return listApplications() .map(Application::getName);} void printApplicationName() { listApplicationNames() .subscribe(System.out::println);}

Page 12: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Using a Mono<T> Response

12

Flux<Application> listApplications() {...}Mono<List<String>> listApplicationNames() { return listApplications() .map(Application::getName) .collectAsList();} Mono<Boolean> doesApplicationExist(String name) { return listApplicationNames() .map(names -> names.contains(name)); }

Page 13: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Every Method Must Return Something

• Unlike many imperative APIs, void is not an appropriate return type

• A reactive flow declares behavior but does not execute it

• Consumers must subscribe to a Flux or Mono in order to begin execution

13

Page 14: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Imperative Operation

14

void delete(String id) { this.restTemplate.delete(URI, id); } public static void main(String[] args) { delete("test-id");}

Page 15: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Reactive Operation

15

Mono<Void> delete(String id) { return this.httpClient.delete(URI, id);} public static void main(String[] args) { CountDownLatch latch = new CountDownLatch(1); delete("test-id") .subscribe(n -> {}, Throwable::printStackTrace, () -> latch.countDown()); latch.await();}

Page 16: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Scope of a Method

• Methods should be designed to be small and reusable

• Reusable methods can be more easily composed into larger operations

• These methods can be more flexibly combined into parallel or sequential operations

16

Page 17: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Reusable Methods

17

Mono<ListApplicationsResponse> getPage(int page) { return this.client.applicationsV2() .list(ListApplicationsRequest.builder() .page(page) .build());} public static void main(String[] args) { getPage(1) .map(PaginatedResponse::getTotalPages) .flatMap(totalPages -> Flux.range(2, totalPages) .flatMap(page -> getPage(page))) .subscribe(System.out::println);}

Page 18: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Implement

Page 19: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Sequential and Parallel Coordination

• All significant performance improvements come from concurrency

• But, concurrency is very hard to do properly

• Reactive frameworks allow you to define sequential and parallel relationships between operations

• The framework does the hard work of coordinating the operations

19

Page 20: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Coordination

20

Mono<ListApplicationsResponse> getPage(int page) { return this.client.applicationsV2() .list(ListApplicationsRequest.builder() .page(page) .build());} public static void main(String[] args) { getPage(1) .map(PaginatedResponse::getTotalPages) .flatMap(totalPages -> Flux.range(2, totalPages) .flatMap(page -> getPage(page))) .subscribe(System.out::println);}

Page 21: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Conditional Logic

• Like normal values, errors pass through the flow’s operations

• These errors can be passed all the way to consumers, or flows can change behavior based on them

• Flows can transform errors or generate new results based on the error

21

Page 22: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Error Logic

22

public Mono<AppStatsResponse> getApp(GetAppRequest request) { return client.applications() .statistics(AppStatsRequest.builder() .applicationId(request.id()) .build()) .otherwise(ExceptionUtils.statusCode(APP_STOPPED_ERROR), t -> Mono.just(AppStatsResponse.builder().build()));}

Page 23: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Conditional Logic

• It is valid for a flow to complete without sending any elements • This is often equivalent to returning null from an

imperative API • This completion can be passed all the way to

consumers • Alternatively, flows can switch behavior or generate

new elements based on the completion

23

Page 24: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Empty Logic

24

public Flux<GetDomainsResponse> getDomains(GetDomainsRequest request) { return requestPrivateDomains(request.getId()) .switchIfEmpty(requestSharedDomains(request.getId));}

Page 25: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Empty Logic

25

public Mono<String> getDomainId(GetDomainIdRequest request) { return getPrivateDomainId(request.getName()) .otherwiseIfEmpty(getSharedDomainId(request.getName())) .otherwiseIfEmpty(ExceptionUtils.illegalState( "Domain %s not found", request.getName()));}

Page 26: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Conditional Logic

• Sometimes you want to make decisions not based on errors or emptiness, but on values themselves

• While it’s possible to implement this logic using operators, it often proves to be more complex than is worthwhile

• It’s OK to use imperative conditional statements

26

Page 27: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Conditional Logic Avoiding Imperative

27

public Mono<String> getDomainId(String domain, String organizationId) { return Mono.just(domain) .filter(d -> d == null) .then(getSharedDomainIds() .switchIfEmpty(getPrivateDomainIds(organizationId)) .next() .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain not found"))) .otherwiseIfEmpty(getPrivateDomainId(domain, organizationId) .otherwiseIfEmpty(getSharedDomainId(domain)) .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain %s not found", domain)));}

Page 28: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Conditional Logic Using Imperative

28

public Mono<String> getDomainId(String domain, String organizationId) { if (domain == null) { return getSharedDomainIds() .switchIfEmpty(getPrivateDomainIds(organizationId)) .next() .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain not found")); } else { return getPrivateDomainId(domain, organizationId) .otherwiseIfEmpty(getSharedDomainId(domain)) .otherwiseIfEmpty(ExceptionUtils.illegalState("Domain %s not found", domain)); }}

Page 29: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Testing

• Most useful flows will be asynchronous • Testing frameworks are aggressively synchronous,

registering results before asynchronous results return

• To compensate for this you must block the main thread and make test assertions in the asynchronous thread

29

Page 30: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Test Finishing Before Assertions

30

@Testpublic void noLatch() { Mono.just("alpha") .subscribeOn(Schedulers.computation()) .subscribe(s -> assertEquals("bravo", s));}

Page 31: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Test Finishing Before Assertions

31

@Testpublic void noLatch() { Mono.just("alpha") .subscribeOn(Schedulers.computation()) .subscribe(s -> assertEquals("bravo", s));}

Page 32: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

@Testpublic void latch() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); AtomicReference<String> actual = new AtomicReference<>(); Mono.just("alpha") .subscribeOn(Schedulers.computation()) .subscribe(actual::set, t -> latch.countDown(), latch::countDown); latch.await(); assertEquals("bravo", actual.get());}

Test Blocking Using CountDownLatch

32

Page 33: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

@Testpublic void latch() throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); AtomicReference<String> actual = new AtomicReference<>(); Mono.just("alpha") .subscribeOn(Schedulers.computation()) .subscribe(actual::set, t -> latch.countDown(), latch::countDown); latch.await(); assertEquals("bravo", actual.get());}

Test Blocking Using CountDownLatch

33

Page 34: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Test Subscriber

• Testing a Reactive design requires more than just blocking • Multiple value assertion • Expected error assertion • Unexpected error failure

• A TestSubscriber<T> can collect this behavior into a single type

34

Page 35: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Test Using TestSubscriber<T>

35

@Testpublic void testSubscriber() throws InterruptedException { TestSubscriber<String> testSubscriber = new TestSubscriber<>(); Flux.just("alpha", "bravo") .subscribeOn(Schedulers.computation()) .subscribe(testSubscriber .assertEquals("alpha") .assertEquals("bravo")); testSubscriber.verify(ofSeconds(5));}

Page 36: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Test Using TestSubscriber<T>

36

@Testpublic void testSubscriber() throws InterruptedException { TestSubscriber<String> testSubscriber = new TestSubscriber<>(); Flux.just("alpha", "bravo") .subscribeOn(Schedulers.computation()) .subscribe(testSubscriber .assertEquals("alpha") .assertEquals("bravo")); testSubscriber.verify(ofSeconds(5));}

Page 37: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Use

Page 38: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Countdown Latches

• Reactive frameworks can only coordinate their own operations

• Many execution environments have processes that outlast individual threads • Reactive programming plays nicely here

• Some execution environments aggressively terminate the process before any individual thread

38

Page 39: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

main() Finishing Before Subscriber<T>

39

public static void main(String[] args) { Mono.just("alpha") .delaySubscription(Duration.ofSeconds(1)) .subscribeOn(Schedulers.computation()) .subscribe(System.out::println);}

Page 40: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

main() Finishing Before Subscriber<T>

40

public static void main(String[] args) { Mono.just("alpha") .delaySubscription(Duration.ofSeconds(1)) .subscribeOn(Schedulers.computation()) .subscribe(System.out::println);}

Page 41: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

main() Waiting for Subscriber<T>

41

public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); Mono.just("alpha") .delaySubscription(Duration.ofSeconds(1)) .subscribeOn(Schedulers.computation()) .subscribe(System.out::println, t -> latch.countDown(), latch::countDown); latch.await();}

Page 42: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

main() Waiting for Subscriber<T>

42

public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); Mono.just("alpha") .delaySubscription(Duration.ofSeconds(1)) .subscribeOn(Schedulers.computation()) .subscribe(System.out::println, t -> latch.countDown(), latch::countDown); latch.await();}

Page 43: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Blocking flows

• It’s very common to bridge between imperative and reactive APIs

• In those cases blocking while waiting for a result is appropriate.

43

Page 44: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Bridging Imperative and Reactive using block()

44

Mono<User> requestUser(String name) {...}

@OverrideUser requestUser(String name) { return getUser(name) .block();}

Page 45: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Bridging Imperative and Reactive using block()

45

Flux<User> requestUsers() {...}

@OverrideList<User> listUsers() { return requestUsers() .collectList() .block();}

Page 46: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Error Handling

• Because errors are values they must be handled, they do not just show up

• Subscribe has 0…3 parameters • Release latches on error

46

Page 47: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Error Handling

47

public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(1); Flux.concat(Mono.just("alpha"), Mono.error(new IllegalStateException())) .subscribe(System.out::println, t -> { t.printStackTrace(); latch.countDown(); }, latch::countDown); latch.await();}

Page 48: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Composable Method References

• Reactive programming is susceptible to callback hell, but it doesn’t have to be

• Extracting behavior into private methods can be even more valuable than in imperative programming

• Well named methods, and method reference syntax, lead to very readable flows

48

Page 49: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Composable Method References

49

@Overridepublic Flux<ApplicationSummary> list() { return Mono .when(this.cloudFoundryClient, this.spaceId) .then(function(DefaultApplications::requestSpaceSummary)) .flatMap(DefaultApplications::extractApplications) .map(DefaultApplications::toApplicationSummary);}

Page 50: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Point Free

• You may have noticed that our code samples use a very compact style • Point Free (http://bit.ly/Point-Free)

• It helps the developer think about composing functions (high level), rather than shuffling data (low level)

• You don’t have to use, but we find that most people prefer it (eventually)

50

Page 51: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Point Free Style

51

Mono<Void> deleteApplication(String name) { return PaginationUtils .requestClientV2Resources(page -> this.client.applicationsV2() .list(ListApplicationsRequest.builder() .name(name) .page(page) .build())) .single() .map(applicationResource -> applicationResource.getMetadata().getId()) .then(applicationId -> this.client.applicationsV2() .delete(DeleteApplicationRequest.builder() .applicationId(applicationId) .build()));}

Page 52: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Summary

Page 53: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Designing, Implementing and Using Reactive APIs

• Design • When to use • Return Mono/Flux • Reusable Methods

• Implement • Sequential and Parallel • Conditional logic • Testing

• Use • Countdown Latches • Blocking flows • Error Handling • Composable methods • Point Free Style

53

Page 54: Designing, Implementing, and Using Reactive APIs

Unless o therw ise ind ica ted , these s l ides are © 2013-2016 P ivo ta l So f tware , Inc . and l i censed under a Creat ive Commons At t r ibu t ion-NonCommerc ia l l i cense: h t tp : / / c rea t ivecommons.org / l i censes /by-nc /3 .0 /

Learn More. Stay Connected.

https://github.com/cloudfoundry/cf-java-client

@springcentral spring.io/blog

@pivotal pivotal.io/blog

@pivotalcf http://engineering.pivotal.io


Recommended