Applying Reactive Programming with RxJava at GOTO Chicago 2015

Applying Reactive Programming with RxJava at GOTO Chicago 2015

Rarely do we have a chance to rewrite an application from scratch with the newest techniques. Generally we must work within our existing architectures evolving and refactoring years of code. This can make it seem reactive programming either can’t be introduced or doesn’t play a role.

Netflix has been doing reactive programming with RxJava in production for several years and only recently embarked on “greenfield” development efforts that are fully async. This talk will leverage the experience of introducing reactive programming into existing imperative, blocking codebases to demonstrate how it can be done and when it can make sense to do so. Not all benefits of reactive programming can be obtained without a greenfield, fully async architecture, but many can. Subjects to be covered will include the mental shift from imperative to declarative, working with blocking IO such as JDBC and RPC, service composition, debugging and unit testing.

Presented at GOTO Chicago 2015 http://gotocon.com/chicago-2015/presentation/Applying%20Reactive%20Programming%20with%20Rx

Video: https://www.youtube.com/watch?v=8OcCSQS0tug

25a69d1e333ff36b77cf01b84b764182?s=128

Ben Christensen

May 11, 2015
Tweet

Transcript

  1. 2.
  2. 3.
  3. 4.
  4. 5.
  5. 6.
  6. 25.

    Observable<Order> getOrders(int customerId) Observable<Product> getProducts(Order order) Observable<ShippingStatus> getShippingStatus(Order o, Product

    p) getOrdersAsync → getProductsAsync → getShippingStatusAsync getOrders → getProducts → getShippingStatus
  7. 26.

    Observable<Order> getOrders(int customerId) Observable<Product> getProducts(Order order) Observable<ShippingStatus> getShippingStatus(Order o, Product

    p) getOrdersAsync → getProductsAsync → getShippingStatusAsync Observable APIs getOrders → getProducts → getShippingStatus
  8. 27.

    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));
  9. 28.

    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
  10. 29.

    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
  11. 30.

    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
  12. 31.

    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));
  13. 33.

    Observable<ShippingStatus> getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return

    Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());
  14. 34.

    Observable<ShippingStatus> getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return

    Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());
  15. 35.

    Observable<ShippingStatus> getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return

    Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());
  16. 36.

    Observable<ShippingStatus> getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return

    Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());
  17. 37.

    Observable<ShippingStatus> getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return

    Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());
  18. 38.

    Observable<ShippingStatus> getShippingStatusAsync(Order o, Product p) return Observable.defer(() -> { return

    Observable.just(getShippingStatus(o, p)); }).subscribeOn(Schedulers.io());
  19. 49.

    List<Order> orders = getOrders(1); try (Connection c = Database.getConnection()) {

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

    List<Order> orders = getOrders(1); try (Connection c = Database.getConnection()) {

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

    Observable<Order> orders = getOrdersAsync(1); return Observable.<Order> 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());
  22. 52.

    Observable<Order> orders = getOrdersAsync(1); return Observable.<Order> 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());
  23. 53.

    return Observable.<Order> 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<Order> orders = getOrdersAsync(1);
  24. 54.

    return Observable.<Order> 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<Order> orders = getOrdersAsync(1);
  25. 55.

    return Observable.<Order> 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<Order> orders = getOrdersAsync(1);
  26. 56.

    return Observable.<Order> 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<Order> orders = getOrdersAsync(1);
  27. 58.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  28. 59.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  29. 60.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  30. 61.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  31. 62.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  32. 63.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  33. 64.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  34. 65.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  35. 66.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  36. 67.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  37. 68.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  38. 69.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  39. 70.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  40. 71.

    return Observable.create(AbstractOnSubscribe.<Order, ResultSet> 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());
  41. 74.

    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 ... } }
  42. 75.

    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 ... } }
  43. 76.

    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 ... } }
  44. 80.

    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 ... } }
  45. 81.

    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 ... } }
  46. 82.

    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 ... } }
  47. 91.

    Iterable<String> is = Arrays.asList("a", "b", "c"); ArrayList<String> ts = new

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

    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(); });
  49. 94.
  50. 95.
  51. 99.

    TestSubscriber<String> 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"));
  52. 100.

    TestSubscriber<String> 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"));
  53. 101.

    TestSubscriber<String> 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"));
  54. 102.

    TestSubscriber<String> 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"));
  55. 103.

    TestSubscriber<String> 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"));
  56. 104.

    TestSubscriber<String> 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"));
  57. 105.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  58. 106.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  59. 107.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  60. 108.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  61. 109.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  62. 110.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  63. 111.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  64. 112.

    TestScheduler test = Schedulers.test(); TestSubscriber<String> 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"));
  65. 114.

    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
  66. 115.

    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
  67. 116.

    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
  68. 117.

    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
  69. 118.

    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
  70. 122.

    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
  71. 123.

    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
  72. 127.
  73. 128.

    class  VideoService  {        def  VideoList  getPersonalizedListOfMovies(userId);  

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

    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 →
  75. 134.

    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 →
  76. 135.

    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
  77. 137.
  78. 152.

    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
  79. 153.

    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
  80. 154.

    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
  81. 155.

    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
  82. 156.

    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
  83. 157.

    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
  84. 158.

    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
  85. 159.

    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
  86. 160.

    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
  87. 161.

    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
  88. 162.

    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
  89. 163.

    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
  90. 164.

    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
  91. 165.

    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 …
  92. 168.

    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
  93. 177.
  94. 178.

    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