Slide 1

Slide 1 text

Ben Christensen Edge Engineering at Netflix @benjchristensen http://techblog.netflix.com/ GOTO Chicago - May 2015 Applying Reactive Programming with RxJava

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

async + callbacks

Slide 8

Slide 8 text

service composition + error handling developer productivity

Slide 9

Slide 9 text

async + callbacks

Slide 10

Slide 10 text

Future Future>

Slide 11

Slide 11 text

Future Future>

Slide 12

Slide 12 text

→ getListOfListOfMovies → forEachList → forEachMovie → getBookmark → getRating → getMetadata → getSimilars → etc

Slide 13

Slide 13 text

Future Future> Future>>

Slide 14

Slide 14 text

Observable Future Future> Future>>

Slide 15

Slide 15 text

Everything is a Stream

Slide 16

Slide 16 text

List getOrders(int customerId) List getProducts(Order order) ShippingStatus getShippingStatus(Order o, Product p) getOrders → getProducts → getShippingStatus

Slide 17

Slide 17 text

List getOrders(int customerId) List getProducts(Order order) ShippingStatus getShippingStatus(Order o, Product p) getOrders → getProducts → getShippingStatus Blocking APIs

Slide 18

Slide 18 text

getOrders → getProducts → getShippingStatus

Slide 19

Slide 19 text

IPC via Apache HTTP Memcached Cassandra Tomcat and Servlets

Slide 20

Slide 20 text

IPC via Apache HTTP Memcached Cassandra Tomcat and Servlets All Blocking

Slide 21

Slide 21 text

Many things could not realistically change

Slide 22

Slide 22 text

We could change our service layer

Slide 23

Slide 23 text

Async Facade

Slide 24

Slide 24 text

Async Facade Blocking Code Servlet Sync/Async Bridge

Slide 25

Slide 25 text

Observable getOrders(int customerId) Observable getProducts(Order order) Observable getShippingStatus(Order o, Product p) getOrdersAsync → getProductsAsync → getShippingStatusAsync getOrders → getProducts → getShippingStatus

Slide 26

Slide 26 text

Observable getOrders(int customerId) Observable getProducts(Order order) Observable getShippingStatus(Order o, Product p) getOrdersAsync → getProductsAsync → getShippingStatusAsync Observable APIs getOrders → getProducts → getShippingStatus

Slide 27

Slide 27 text

getOrdersAsync(1) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }) .forEach(s -> System.out.println(Thread.currentThread() + " [Async] Shipping Status: " + s));

Slide 28

Slide 28 text

getOrdersAsync(1) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }) .forEach(s -> System.out.println(Thread.currentThread() + " [Async] Shipping Status: " + s)); an async Observable

Slide 29

Slide 29 text

getOrdersAsync(1) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }) .forEach(s -> System.out.println(Thread.currentThread() + " [Async] Shipping Status: " + s)); an async Observable stream

Slide 30

Slide 30 text

getOrdersAsync(1) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }) .forEach(s -> System.out.println(Thread.currentThread() + " [Async] Shipping Status: " + s)); another async Observable stream

Slide 31

Slide 31 text

getOrdersAsync(1) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }) .forEach(s -> System.out.println(Thread.currentThread() + " [Async] Shipping Status: " + s));

Slide 32

Slide 32 text

Observable getShippingStatusAsync(Order o, Product p) ShippingStatus getShippingStatus(Order o, Product p)

Slide 33

Slide 33 text

Observable getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());

Slide 34

Slide 34 text

Observable getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());

Slide 35

Slide 35 text

Observable getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());

Slide 36

Slide 36 text

Observable getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());

Slide 37

Slide 37 text

Observable getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());

Slide 38

Slide 38 text

Observable getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());

Slide 39

Slide 39 text

List orders = getOrders(1); Observable orders = getOrdersAsync(1);

Slide 40

Slide 40 text

List orders = getOrders(1); Observable orders = getOrdersAsync(1);

Slide 41

Slide 41 text

List orders = getOrders(1); Observable orders = getOrdersAsync(1);

Slide 42

Slide 42 text

return Observable.defer(() -> { return Observable.from(getOrders(customerId)); }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 43

Slide 43 text

return Observable.defer(() -> { return Observable.from(getOrders(customerId)); }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 44

Slide 44 text

Observable orders = getOrdersAsync(1); return Observable.defer(() -> { return Observable.from(getOrders(customerId)); }).subscribeOn(Schedulers.io());

Slide 45

Slide 45 text

return Observable.defer(() -> { return Observable.from(getOrders(customerId)); }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 46

Slide 46 text

return Observable.defer(() -> { return Observable.from(getOrders(customerId)); }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 47

Slide 47 text

Wrapped with Threads using subscribeOn

Slide 48

Slide 48 text

Wrapped with Threads JDBC?

Slide 49

Slide 49 text

List orders = getOrders(1); try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); ArrayList orders = new ArrayList<>(); while (rs.next()) { orders.add(new Order(rs.getInt(1))); } rs.close(); return orders; } catch (Exception e) { throw new RuntimeException(e); }

Slide 50

Slide 50 text

List orders = getOrders(1); try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); ArrayList orders = new ArrayList<>(); while (rs.next()) { orders.add(new Order(rs.getInt(1))); } rs.close(); return orders; } catch (Exception e) { throw new RuntimeException(e); }

Slide 51

Slide 51 text

Observable orders = getOrdersAsync(1); return Observable. create(o -> { try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); while (rs.next() && !o.isUnsubscribed()) { o.onNext(new Order(rs.getInt(1))); } rs.close(); o.onCompleted(); } catch (Exception e) { o.onError(e); } }).subscribeOn(Schedulers.io());

Slide 52

Slide 52 text

Observable orders = getOrdersAsync(1); return Observable. create(o -> { try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); while (rs.next() && !o.isUnsubscribed()) { o.onNext(new Order(rs.getInt(1))); } rs.close(); o.onCompleted(); } catch (Exception e) { o.onError(e); } }).subscribeOn(Schedulers.io());

Slide 53

Slide 53 text

return Observable. create(o -> { try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); while (rs.next() && !o.isUnsubscribed()) { o.onNext(new Order(rs.getInt(1))); } rs.close(); o.onCompleted(); } catch (Exception e) { o.onError(e); } }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 54

Slide 54 text

return Observable. create(o -> { try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); while (rs.next() && !o.isUnsubscribed()) { o.onNext(new Order(rs.getInt(1))); } rs.close(); o.onCompleted(); } catch (Exception e) { o.onError(e); } }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 55

Slide 55 text

return Observable. create(o -> { try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); while (rs.next() && !o.isUnsubscribed()) { o.onNext(new Order(rs.getInt(1))); } rs.close(); o.onCompleted(); } catch (Exception e) { o.onError(e); } }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 56

Slide 56 text

return Observable. create(o -> { try (Connection c = Database.getConnection()) { ResultSet rs = c.createStatement() .executeQuery("select * from orders"); while (rs.next() && !o.isUnsubscribed()) { o.onNext(new Order(rs.getInt(1))); } rs.close(); o.onCompleted(); } catch (Exception e) { o.onError(e); } }).subscribeOn(Schedulers.io()); Observable orders = getOrdersAsync(1);

Slide 57

Slide 57 text

or with “reactive pull” backpressure …

Slide 58

Slide 58 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 59

Slide 59 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 60

Slide 60 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 61

Slide 61 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 62

Slide 62 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 63

Slide 63 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 64

Slide 64 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 65

Slide 65 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 66

Slide 66 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 67

Slide 67 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 68

Slide 68 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 69

Slide 69 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 70

Slide 70 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 71

Slide 71 text

return Observable.create(AbstractOnSubscribe. create(s -> { ResultSet rs = s.state(); try { if (rs.next()) { s.onNext(new Order(rs.getInt(1))); } else { s.onCompleted(); } } catch (SQLException e) { s.onError(e); } }, s -> { Connection c = Database.getConnection(); try { Statement stmt = c.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE); return stmt.executeQuery("select * from orders"); } catch (SQLException e) { s.onError(e); return null; } }, rs -> rs.close() } // ignoring try/catch for slide brevity )).subscribeOn(Schedulers.io());

Slide 72

Slide 72 text

Observable.defer Observable.from/just Observable.subscribeOn Observable.create

Slide 73

Slide 73 text

Async Facade Blocking Code Servlet Sync/Async Bridge

Slide 74

Slide 74 text

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // perform service composition asynchronously getShippingStatusForCustomerOrdersAsync(req.getParameter(“customerId")) .toBlocking() // block on servlet thread .forEach(status -> { try { // write output using blocking IO resp.getWriter().write(status.toString()); } catch (Exception e) { throw new RuntimeException("unable to write", e); } }); } catch (Exception e) { // toBlocking and forEach will convert async onError events into thrown exceptions resp.setStatus(500); // write and log error ... } }

Slide 75

Slide 75 text

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // perform service composition asynchronously getShippingStatusForCustomerOrdersAsync(req.getParameter("customerId")) .toBlocking() // block on servlet thread .forEach(status -> { try { // write output using blocking IO resp.getWriter().write(status.toString()); } catch (Exception e) { throw new RuntimeException("unable to write", e); } }); } catch (Exception e) { // toBlocking and forEach will convert async onError events into thrown exceptions resp.setStatus(500); // write and log error ... } }

Slide 76

Slide 76 text

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // perform service composition asynchronously getShippingStatusForCustomerOrdersAsync(req.getParameter("customerId")) .toBlocking() // block on servlet thread .forEach(status -> { try { // write output using blocking IO resp.getWriter().write(status.toString()); } catch (Exception e) { throw new RuntimeException("unable to write", e); } }); } catch (Exception e) { // toBlocking and forEach will convert async onError events into thrown exceptions resp.setStatus(500); // write and log error ... } }

Slide 77

Slide 77 text

Observable getShippingStatusForCustomerOrdersAsync(String customerId) { return getOrdersAsync(Integer.parseInt(customerId)) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }); }

Slide 78

Slide 78 text

Observable getShippingStatusForCustomerOrdersAsync(String customerId) { return getOrdersAsync(Integer.parseInt(customerId)) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }); }

Slide 79

Slide 79 text

Observable getShippingStatusForCustomerOrdersAsync(String customerId) { return getOrdersAsync(Integer.parseInt(customerId)) .limit(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }); }

Slide 80

Slide 80 text

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // perform service composition asynchronously getShippingStatusForCustomerOrdersAsync(req.getParameter("customerId")) .toBlocking() // block on servlet thread .forEach(status -> { try { // write output using blocking IO resp.getWriter().write(status.toString()); } catch (Exception e) { throw new RuntimeException("unable to write", e); } }); } catch (Exception e) { // toBlocking and forEach will convert async onError events into thrown exceptions resp.setStatus(500); // write and log error ... } }

Slide 81

Slide 81 text

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // perform service composition asynchronously getShippingStatusForCustomerOrdersAsync(req.getParameter("customerId")) .toBlocking() // block on servlet thread .forEach(status -> { try { // write output using blocking IO resp.getWriter().write(status.toString()); } catch (Exception e) { throw new RuntimeException("unable to write", e); } }); } catch (Exception e) { // toBlocking and forEach will convert async onError events into thrown exceptions resp.setStatus(500); // write and log error ... } }

Slide 82

Slide 82 text

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { // perform service composition asynchronously getShippingStatusForCustomerOrdersAsync(req.getParameter("customerId")) .toBlocking() // block on servlet thread .forEach(status -> { try { // write output using blocking IO resp.getWriter().write(status.toString()); } catch (Exception e) { throw new RuntimeException("unable to write", e); } }); } catch (Exception e) { // toBlocking and forEach will convert async onError events into thrown exceptions resp.setStatus(500); // write and log error ... } }

Slide 83

Slide 83 text

Observable.toBlocking

Slide 84

Slide 84 text

Observable.toBlocking for bridging between async and sync

Slide 85

Slide 85 text

So how did it go adding reactive to an existing codebase?

Slide 86

Slide 86 text

First attempt took 3 tries

Slide 87

Slide 87 text

What the *&!@#$^!!!

Slide 88

Slide 88 text

Tech Worked …

Slide 89

Slide 89 text

… but …

Slide 90

Slide 90 text

Needed to relearn idiomatic solutions

Slide 91

Slide 91 text

Iterable is = Arrays.asList("a", "b", "c"); ArrayList ts = new ArrayList<>(); for(String s : is) { ts.add(s + "-modified"); } // do stuff with ts ... Observable os = Observable.just("a", "b", "c"); os.map(s -> s + "-modified"); // do stuff with os ...

Slide 92

Slide 92 text

try { for (String s : is) { ts.add(s + "-modified"); } } catch (Exception e) { // do something with exception } os.map(s -> s + "-modified") .onErrorResumeNext(exception -> { // do something with exception return Observable.empty(); });

Slide 93

Slide 93 text

Needed to invest in documentation

Slide 94

Slide 94 text

No content

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

Needed to allow for time and mistakes

Slide 97

Slide 97 text

Also … Unit Testing & Debugging

Slide 98

Slide 98 text

Async Unit Tests

Slide 99

Slide 99 text

TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS) .take(5) .map(i -> { return i + " value"; }).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value", "2 value", "3 value", "4 value"));

Slide 100

Slide 100 text

TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS) .take(5) .map(i -> { return i + " value"; }).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value", "2 value", "3 value", "4 value"));

Slide 101

Slide 101 text

TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS) .take(5) .map(i -> { return i + " value"; }).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value", "2 value", "3 value", "4 value"));

Slide 102

Slide 102 text

TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS) .take(5) .map(i -> { return i + " value"; }).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value", "2 value", "3 value", "4 value"));

Slide 103

Slide 103 text

TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS) .take(5) .map(i -> { return i + " value"; }).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value", "2 value", "3 value", "4 value"));

Slide 104

Slide 104 text

TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS) .take(5) .map(i -> { return i + " value"; }).subscribe(ts); ts.awaitTerminalEvent(); ts.assertNoErrors(); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value", "2 value", "3 value", "4 value"));

Slide 105

Slide 105 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 106

Slide 106 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 107

Slide 107 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 108

Slide 108 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 109

Slide 109 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 110

Slide 110 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 111

Slide 111 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 112

Slide 112 text

TestScheduler test = Schedulers.test(); TestSubscriber ts = new TestSubscriber<>(); Observable.interval(200, TimeUnit.MILLISECONDS, test) .map(i -> { return i + " value"; }).subscribe(ts); test.advanceTimeBy(200, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value")); test.advanceTimeTo(1000, TimeUnit.MILLISECONDS); ts.assertReceivedOnNext(Arrays.asList("0 value", "1 value”, "2 value", "3 value", "4 value"));

Slide 113

Slide 113 text

Async Debugging is Hard

Slide 114

Slide 114 text

java.lang.RuntimeException: failure! at jdbc.Test.lambda$19(Test.java:63) at jdbc.Test$$Lambda$8/888452645.call(Unknown Source) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55) at rx.internal.operators.OperatorSubscribeOn$1$1$1.onNext(OperatorSubscribeOn.java:76) at rx.observables.AbstractOnSubscribe$SubscriptionState.accept(AbstractOnSubscribe.java:533) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.doNext(AbstractOnSubscribe.java:367) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.request(AbstractOnSubscribe.java:345) at rx.internal.operators.OperatorSubscribeOn$1$1$1$1.request(OperatorSubscribeOn.java:88) at rx.Subscriber.setProducer(Subscriber.java:177) at rx.Subscriber.setProducer(Subscriber.java:171) at rx.internal.operators.OperatorSubscribeOn$1$1$1.setProducer(OperatorSubscribeOn.java:81) at rx.observables.AbstractOnSubscribe.call(AbstractOnSubscribe.java:191) at rx.observables.AbstractOnSubscribe$LambdaOnSubscribe.call(AbstractOnSubscribe.java:274) at rx.Observable.unsafeSubscribe(Observable.java:7495) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: jdbc.Product.class at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:98) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:58) ... 20 more Ugly Stacktraces

Slide 115

Slide 115 text

java.lang.RuntimeException: failure! at jdbc.Test.lambda$19(Test.java:63) at jdbc.Test$$Lambda$8/888452645.call(Unknown Source) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55) at rx.internal.operators.OperatorSubscribeOn$1$1$1.onNext(OperatorSubscribeOn.java:76) at rx.observables.AbstractOnSubscribe$SubscriptionState.accept(AbstractOnSubscribe.java:533) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.doNext(AbstractOnSubscribe.java:367) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.request(AbstractOnSubscribe.java:345) at rx.internal.operators.OperatorSubscribeOn$1$1$1$1.request(OperatorSubscribeOn.java:88) at rx.Subscriber.setProducer(Subscriber.java:177) at rx.Subscriber.setProducer(Subscriber.java:171) at rx.internal.operators.OperatorSubscribeOn$1$1$1.setProducer(OperatorSubscribeOn.java:81) at rx.observables.AbstractOnSubscribe.call(AbstractOnSubscribe.java:191) at rx.observables.AbstractOnSubscribe$LambdaOnSubscribe.call(AbstractOnSubscribe.java:274) at rx.Observable.unsafeSubscribe(Observable.java:7495) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: jdbc.Product.class at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:98) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:58) ... 20 more Ugly Stacktraces

Slide 116

Slide 116 text

java.lang.RuntimeException: failure! at jdbc.Test.lambda$19(Test.java:63) at jdbc.Test$$Lambda$8/888452645.call(Unknown Source) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55) at rx.internal.operators.OperatorSubscribeOn$1$1$1.onNext(OperatorSubscribeOn.java:76) at rx.observables.AbstractOnSubscribe$SubscriptionState.accept(AbstractOnSubscribe.java:533) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.doNext(AbstractOnSubscribe.java:367) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.request(AbstractOnSubscribe.java:345) at rx.internal.operators.OperatorSubscribeOn$1$1$1$1.request(OperatorSubscribeOn.java:88) at rx.Subscriber.setProducer(Subscriber.java:177) at rx.Subscriber.setProducer(Subscriber.java:171) at rx.internal.operators.OperatorSubscribeOn$1$1$1.setProducer(OperatorSubscribeOn.java:81) at rx.observables.AbstractOnSubscribe.call(AbstractOnSubscribe.java:191) at rx.observables.AbstractOnSubscribe$LambdaOnSubscribe.call(AbstractOnSubscribe.java:274) at rx.Observable.unsafeSubscribe(Observable.java:7495) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: jdbc.Product.class at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:98) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:58) ... 20 more Missing Async Call Chain

Slide 117

Slide 117 text

java.lang.RuntimeException: failure! at jdbc.Test.lambda$19(Test.java:63) at jdbc.Test$$Lambda$8/888452645.call(Unknown Source) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55) at rx.internal.operators.OperatorSubscribeOn$1$1$1.onNext(OperatorSubscribeOn.java:76) at rx.observables.AbstractOnSubscribe$SubscriptionState.accept(AbstractOnSubscribe.java:533) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.doNext(AbstractOnSubscribe.java:367) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.request(AbstractOnSubscribe.java:345) at rx.internal.operators.OperatorSubscribeOn$1$1$1$1.request(OperatorSubscribeOn.java:88) at rx.Subscriber.setProducer(Subscriber.java:177) at rx.Subscriber.setProducer(Subscriber.java:171) at rx.internal.operators.OperatorSubscribeOn$1$1$1.setProducer(OperatorSubscribeOn.java:81) at rx.observables.AbstractOnSubscribe.call(AbstractOnSubscribe.java:191) at rx.observables.AbstractOnSubscribe$LambdaOnSubscribe.call(AbstractOnSubscribe.java:274) at rx.Observable.unsafeSubscribe(Observable.java:7495) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: jdbc.Product.class at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:98) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:58) ... 20 more Stack Pollution

Slide 118

Slide 118 text

java.lang.RuntimeException: failure! at jdbc.Test.lambda$19(Test.java:63) at jdbc.Test$$Lambda$8/888452645.call(Unknown Source) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:55) at rx.internal.operators.OperatorSubscribeOn$1$1$1.onNext(OperatorSubscribeOn.java:76) at rx.observables.AbstractOnSubscribe$SubscriptionState.accept(AbstractOnSubscribe.java:533) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.doNext(AbstractOnSubscribe.java:367) at rx.observables.AbstractOnSubscribe$SubscriptionProducer.request(AbstractOnSubscribe.java:345) at rx.internal.operators.OperatorSubscribeOn$1$1$1$1.request(OperatorSubscribeOn.java:88) at rx.Subscriber.setProducer(Subscriber.java:177) at rx.Subscriber.setProducer(Subscriber.java:171) at rx.internal.operators.OperatorSubscribeOn$1$1$1.setProducer(OperatorSubscribeOn.java:81) at rx.observables.AbstractOnSubscribe.call(AbstractOnSubscribe.java:191) at rx.observables.AbstractOnSubscribe$LambdaOnSubscribe.call(AbstractOnSubscribe.java:274) at rx.Observable.unsafeSubscribe(Observable.java:7495) at rx.internal.operators.OperatorSubscribeOn$1$1.call(OperatorSubscribeOn.java:62) at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: rx.exceptions.OnErrorThrowable$OnNextValue: OnError while emitting onNext value: jdbc.Product.class at rx.exceptions.OnErrorThrowable.addValueAsLastCause(OnErrorThrowable.java:98) at rx.internal.operators.OperatorMap$1.onNext(OperatorMap.java:58) ... 20 more RxJava Tries

Slide 119

Slide 119 text

Stepping Severely Limited

Slide 120

Slide 120 text

Stepping Severely Limited

Slide 121

Slide 121 text

Stepping Severely Limited

Slide 122

Slide 122 text

orders.flatMap(o -> { return getProductsAsync(o) .doOnNext(t -> System.out.println("product: " + t)) .flatMap(p -> { return getShippingStatusAsync(o, p) .map(s -> { return s; }); }); }) Debugging via Logging

Slide 123

Slide 123 text

orders.flatMap(o -> { return getProductsAsync(o) .doOnNext(t -> System.out.println("product: " + t)) .flatMap(p -> { return getShippingStatusAsync(o, p) .map(s -> { return s; }); }); }) :-( Debugging via Logging

Slide 124

Slide 124 text

Async Debugging is Hard … a work in progress …

Slide 125

Slide 125 text

How about once all done?

Slide 126

Slide 126 text

Async Facade Blocking Code Servlet Sync/Async Bridge Observable APIs Worked Well

Slide 127

Slide 127 text

No content

Slide 128

Slide 128 text

class  VideoService  {        def  VideoList  getPersonalizedListOfMovies(userId);        def  VideoBookmark  getBookmark(userId,  videoId);        def  VideoRating  getRating(userId,  videoId);        def  VideoMetadata  getMetadata(videoId);   } class  VideoService  {        def  Observable  getPersonalizedListOfMovies(userId);        def  Observable  getBookmark(userId,  videoId);        def  Observable  getRating(userId,  videoId);        def  Observable  getMetadata(videoId);   } ... with an observable api: replaced the blocking api ...

Slide 129

Slide 129 text

Async Facade Blocking Code Servlet Sync/Async Bridge Challenges Mixing Approaches

Slide 130

Slide 130 text

Async Facade Blocking Code Servlet Sync/Async Bridge too many threads

Slide 131

Slide 131 text

Async Facade Blocking Code Servlet Sync/Async Bridge hard to tune & shed load

Slide 132

Slide 132 text

Async Facade Blocking Code Servlet Sync/Async Bridge easy to block async code

Slide 133

Slide 133 text

getOrdersAsync(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }); getOrdersAsync(1) .flatMap(o -> { return getProductsAsync(o) .map(p -> { return getShippingStatus(o, p); }); }); concurrent → sequential →

Slide 134

Slide 134 text

getOrdersAsync(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }); getOrdersAsync(1) .flatMap(o -> { return getProductsAsync(o) .map(p -> { return getShippingStatus(o, p); }); }); concurrent → sequential →

Slide 135

Slide 135 text

getOrdersAsync(1) .flatMap(o -> { return getProductsAsync(o) .flatMap(p -> { return getShippingStatusAsync(o, p); }); }); getOrdersAsync(1) .flatMap(o -> { return getProductsAsync(o) .map(p -> { return getShippingStatus(o, p); }); }); Async Facade only has Observable APIs

Slide 136

Slide 136 text

So what about going “fully reactive”?

Slide 137

Slide 137 text

No content

Slide 138

Slide 138 text

“cause it’s better”

Slide 139

Slide 139 text

“cause it’s better” :-/

Slide 140

Slide 140 text

“it’s worse”

Slide 141

Slide 141 text

“it doesn’t matter”

Slide 142

Slide 142 text

green threads, native threads, fibers, actors, event loops, theoretically equivalent, OS kernels, etc, etc, etc …

Slide 143

Slide 143 text

C10k and beyond

Slide 144

Slide 144 text

What to bet future on?

Slide 145

Slide 145 text

What to bet future on?

Slide 146

Slide 146 text

WSPerfLab: https://github.com/Netflix-Skunkworks/WSPerfLab

Slide 147

Slide 147 text

WSPerfLab: https://github.com/Netflix-Skunkworks/WSPerfLab

Slide 148

Slide 148 text

WSPerfLab: https://github.com/Netflix-Skunkworks/WSPerfLab

Slide 149

Slide 149 text

WSPerfLab: https://github.com/Netflix-Skunkworks/WSPerfLab

Slide 150

Slide 150 text

WSPerfLab: https://github.com/Netflix-Skunkworks/WSPerfLab

Slide 151

Slide 151 text

WSPerfLab: https://github.com/Netflix-Skunkworks/WSPerfLab 154ms

Slide 152

Slide 152 text

Intel(R) Core(TM) i5-2400 CPU @ 3.10GHz: 4 cores, 1 thread per core OpenJDK 8 with frame pointer patch RxNetty 0.4.8 with Netty 4.0.25.Final vs Tomcat 7.0.45

Slide 153

Slide 153 text

0% 25% 50% 75% 100% 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat CPU Usage in Percent concurrent clients Testing drove both tests near 100% cpu usage

Slide 154

Slide 154 text

0ms 0.225ms 0.45ms 0.675ms 0.9ms 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat CPU Consumption per Request concurrent clients • Netty has lower CPU consumption per request • Netty keeps getting faster under load, Tomcat gets slower

Slide 155

Slide 155 text

0rps 1250rps 2500rps 3750rps 5000rps 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat Throughput concurrent clients • Netty achieves higher throughput • Mostly due to lower CPU consumption per request

Slide 156

Slide 156 text

0ms 70ms 140ms 210ms 280ms 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat Average Latency concurrent clients Theoretical best possible for test case is 154ms

Slide 157

Slide 157 text

0ms 750ms 1500ms 2250ms 3000ms 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat Max Latency concurrent clients Tomcat latency degradation is far more severe than Netty

Slide 158

Slide 158 text

0ipc 0.175ipc 0.35ipc 0.525ipc 0.7ipc 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat CPU Instructions per Cycle concurrent clients Netty increases instructions per cycle as load increases

Slide 159

Slide 159 text

0 400000 800000 1200000 1600000 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat Thread Migrations concurrent clients significant divergence

Slide 160

Slide 160 text

0 400000 800000 1200000 1600000 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat Thread Migrations concurrent clients Under light load both are similar

Slide 161

Slide 161 text

0 400000 800000 1200000 1600000 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat Thread Migrations concurrent clients Increased load causes less migrations, improving IPC and CPU usage per request with event loop architecture

Slide 162

Slide 162 text

Why? • More efficient framework code • object allocation rates and GC costs • Event loop architecture • reduced thread migrations • cache warmth → memory locality → instructions per cycle → lower cpu consumption per request • Thread pool architecture • lock contention • thread migrations

Slide 163

Slide 163 text

Why? • More efficient framework code • object allocation rates and GC costs • Event loop architecture • reduced thread migrations • cache warmth → memory locality → instructions per cycle → lower cpu consumption per request • Thread pool architecture • lock contention • thread migrations

Slide 164

Slide 164 text

Why? • More efficient framework code • object allocation rates and GC costs • Event loop architecture • reduced thread migrations • cache warmth → memory locality → instructions per cycle → lower cpu consumption per request • Thread pool architecture • lock contention • thread migrations

Slide 165

Slide 165 text

Why? • More efficient framework code • object allocation rates and GC costs • Event loop architecture • reduced thread migrations • cache warmth → memory locality → instructions per cycle → lower cpu consumption per request • Thread pool architecture • lock contention • thread migrations • Many more details …

Slide 166

Slide 166 text

With current JVM and Linux, event loops have efficiency benefits.

Slide 167

Slide 167 text

Simpler But Not Easier

Slide 168

Slide 168 text

0ms 750ms 1500ms 2250ms 3000ms 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 100010501100 RxNetty Tomcat Simpler But Not Easier

Slide 169

Slide 169 text

A fully async architecture does have benefits.

Slide 170

Slide 170 text

Reactive programming can be incrementally applied.

Slide 171

Slide 171 text

Mixed Codebases

Slide 172

Slide 172 text

Concurrency via Async Service Composition

Slide 173

Slide 173 text

Leverage RxJava Schedulers and Threads

Slide 174

Slide 174 text

Leverage RxJava Schedulers and Threads (or use Hystrix)

Slide 175

Slide 175 text

Concurrency is still non-trivial

Slide 176

Slide 176 text

Rx doesn’t trivialize it Concurrency is still non-trivial

Slide 177

Slide 177 text

No content

Slide 178

Slide 178 text

Reactive Programming in the Netflix API with RxJava http://techblog.netflix.com/2013/02/rxjava-netflix-api.html Optimizing the Netflix API http://techblog.netflix.com/2013/01/optimizing-netflix-api.html Reactive Extensions (Rx) http://www.reactivex.io Reactive Streams https://github.com/reactive-streams/reactive-streams Ben Christensen @benjchristensen RxJava https://github.com/ReactiveX/RxJava @RxJava RxJS http://reactive-extensions.github.io/RxJS/ @ReactiveX jobs.netflix.com Dragon Image: http://www.clipartpanda.com/clipart_images/you-can-use-this-clip-art-on-2194929 Greenfield Image: https://kvaes.wordpress.com/2013/06/05/lingo-explained-greenfield-vs-brownfield/ NY City Image: https://thesandincalifornia.files.wordpress.com/2012/05/newyorkcity-madness.jpg Futuristic City: http://i.imgur.com/rPTkr9D.jpg