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

Multi-service reactive streams using Spring, Reactor, RSocket

235 Aufrufe

Veröffentlicht am

Delivered at Oracle CodeOne

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

Multi-service reactive streams using Spring, Reactor, RSocket

  1. 1. Multi-Service Reactive Streams using RSocket, Reactor, and Spring Ben Hale, Cloud Foundry Java Experience Lead @nebhale Stephane Maldini, Project Reactor Lead @smaldini
  2. 2. Reactive Programming • Reactive programming is the next frontier in Java for high-efficiency applications • Fundamentally non-blocking and often paired with asynchronous behaviors • Reactive has no opinion on async and many flows are totally synchronous • Key differentiator from "async" is Reactive (pull-push) back pressure
  3. 3. WebFlux and WebClient @GetMapping("/health") Mono<Health> compositeHealth() { return Mono.zip( webClient.get().uri("https://alpha-service/health") .retrieve().bodyToMono(Health.class), webClient.get().uri("https://bravo-service/health") .retrieve().bodyToMono(Health.class)) .map(t -> composite(t.getT1(), t.getT2())); }
  4. 4. Roadblocks • But there are still some barriers to using Reactive everywhere* • Data Access • MongoDB, Apache Cassandra, and Redis • No relational database access • Cross-process back pressure (networking)
  5. 5. Roadblocks • But there are still some barriers to using Reactive everywhere* • Data Access • MongoDB, Apache Cassandra, and Redis • No relational database access • Cross-process back pressure (networking) Until R2DBC ! Check out r2dbc.io • No relational database access
  6. 6. http://rsocket.io
  7. 7. Message Driven Binary Protocol • Requester-Responder interaction is broken down into frames that encapsulate messages • The framing is binary (not human readable like JSON or XML) • Massive efficiencies for machine-to-machine communication • Downsides only manifest rarely and can be mitigated with tooling • Payloads are bags of bytes • Can be JSON or XML (it's all just 1's and 0's)
  8. 8. request/reply request/void (fire&forget) request/stream stream/stream (channel) 4 defined interaction models
  9. 9. request/replyrequest/reply
  10. 10. request/reply request/reply
  11. 11. request/reply request/replyrequest/reply
  12. 12. request/reply request/replyrequest/reply
  13. 13. request/reply request/replyrequest/reply 🔎multiplexed
  14. 14. 🔎transport agnostic 
 e.g. Websocket
  15. 15. request/streamrequest/stream
  16. 16. request/streamrequest/stream
  17. 17. request/stream 🔎 🔎 bidirectional
  18. 18. Bi-Directional • Many protocols (notably not TCP) have a distinction between the client and server for the lifetime of a connection • This division means that one side of the connection must initiate all requests, and the other side must initiate all responses • Even more flexible protocols like HTTP/2 do not fully drop the distinction • Servers cannot start an unrequested stream of data to the client • Once a client initiates a connection to a server, both parties can be requestors or responders to a logical stream
  19. 19. 2 per-message
 flow-control
  20. 20. 2 2 per-message
 flow-control
  21. 21. 2 per-message
 flow-control
  22. 22. 00 per-message
 flow-control
  23. 23. 0 per-message
 flow-control
  24. 24. Reactive Streams Back Pressure • Network protocols generally send a single request, and receive an arbitrarily large response in return • There is nothing to stop the responder (or even the requestor) from sending an arbitrarily large amount of data and overwhelming the receiver • In cases where TCP back pressure throttles the responder, queues fill with large amounts of un-transferred data • Reactive Streams (pull-push) back pressure ensures that data is only materialized and transferred when receiver is ready to process it
  25. 25. 20 0
  26. 26. 302 0 21
  27. 27. 3 2 2 2
  28. 28. 3 22
  29. 29. 23 2 3
  30. 30. 3 2 3 ❌
  31. 31. 3 22
  32. 32. 3 2 Resumption 22
  33. 33. 3 23 3 Resumption
  34. 34. 3 33 3 Resumption
  35. 35. Resumption/Resumability • Starting as a client-to-edge-server protocol highlighted a common failing of existing options • Clients on unstable connections would often drop and need to re-establish current state • Led to inefficiencies in both network traffic and data-center compute • Resumability allows both parties in a "logical connection" to identify themselves on reconnection • On Resumption both parties handshake about the last frame received and all missed frames are re-transmitted • Frame caching to support is not defined by spec so it can be very flexible 36
  36. 36. language agnostic
  37. 37. compose with no semantics loss 🔎 🔎 🔎 ws tcp udp
  38. 38. RSocket Protocol TCP WebSocket Aeron/UDPHTTP/2 Protobuf JSON Custom Binary RPC-style Messaging Java JavaScript C++ Kotlin Flow
  39. 39. Using the RSocket API
  40. 40. Java API public interface RSocket { Mono<Payload> requestResponse(Payload payload); Mono<Void> fireAndForget(Payload payload); Flux<Payload> requestStream(Payload payload); Flux<Payload> requestChannel(Flux<Payload> payloads); }
  41. 41. Java API public interface RSocket { Mono<Payload> requestResponse(Payload payload); Mono<Void> fireAndForget(Payload payload); Flux<Payload> requestStream(Payload payload); Flux<Payload> requestChannel(Flux<Payload> payloads); }
  42. 42. Interaction Models – Request-Response Mono<Payload> resp = client.requestResponse(requestPayload) • Standard Request-Response semantics • Likely to represent the majority of requests for the foreseeable future • Even this obvious interaction model surpasses HTTP because it is asynchronous and multiplexed • Request with account number, respond with account balance
  43. 43. Interaction Models – Fire-and-Forget Mono<Void> resp = client.fireAndForget(requestPayload) • An optimization of Request-Response when a response isn't necessary • Significant efficiencies • Networking (no ack) • Client/Server processing (immediate release of resources) • Non-critical event logging
  44. 44. Interaction Models – Request-Stream Flux<Payload> resp = client.requestStream(requestPayload) • Analogous to Request-Response returning a collection • The collection is streamed back instead of queuing until complete • RequestN semantics mean data is not materialized until ready to send • Request with account number, respond with real-time stream of account transactions
  45. 45. Interaction Models – Channel Flux<Payload> out = client.requestChannel(Flux<Payload> in) • A bi-directional stream of messages in both directions • An unstructured channel allows arbitrary interaction models • Request burst of initial state, listen for subsequent updates, client updates subscription without starting new connection • Request with account number, respond with real-time stream of account transactions, update subscription to filter certain transaction types, respond with filtered real-time stream of account transactions
  46. 46. Raw Client RSocket client = RSocketFactory.connect() .transport(TcpClientTransport.create("1.2.3.4", 80)) .start() .block();
  47. 47. Raw Client RSocket client = RSocketFactory.connect() .transport(TcpClientTransport.create("1.2.3.4", 80)) .start() .block(); client.requestResponse(new DefaultPayload(…)) .doOnComplete(() -> System.out.println(“hooray”) .subscribe();
  48. 48. Raw Client RSocket client = RSocketFactory.connect() .transport(TcpClientTransport.create("1.2.3.4", 80)) .start() .block(); client.requestResponse(new DefaultPayload(…)) .doOnComplete(() -> System.out.println(“hooray”) .subscribe(); Censored ByteBuffer creation
  49. 49. Raw Client Too Low Level ?
 Shift to the next gear with existing tech built on RSocket 😱
  50. 50. Building applications with RSocket API • Programming Model Agnostic • The RSocket interface is a serviceable programming model but not great • Designed to be a building block that multiple other programming models could build upon
  51. 51. Building applications with RSocket API • Making things simpler: • RPC-style (protobuf code generation) • Messaging-style (Spring message handlers/controllers)
  52. 52. RPC-style (Contract Driven) service RecordsService { rpc records (RecordsRequest) returns (stream Record) {} }
  53. 53. RPC-style (Contract Driven) service RecordsService { rpc records (RecordsRequest) returns (stream Record) {} } RecordsServiceClient rankingService = new RecordsServiceClient(rsocket); recordsService.records(RecordsRequest.newBuilder() .setMaxResults(16) .build()) .subscribe(record -> System.out.println(record));
  54. 54. RPC-style (Contract Driven) service RecordsService { rpc records (RecordsRequest) returns (stream Record) {} } RecordsServiceClient rankingService = new RecordsServiceClient(rsocket); recordsService.records(RecordsRequest.newBuilder() .setMaxResults(16) .build()) .subscribe(record -> System.out.println(record)); You still need to manage this part
  55. 55. RPC-style (Contract Driven) service RecordsService { rpc records (RecordsRequest) returns (stream Record) {} }
  56. 56. RPC-style (Contract Driven) service RecordsService { rpc records (RecordsRequest) returns (stream Record) {} } let recordServiceClient = new RecordsServiceClient(rsocket); let req = new RecordRequest(); req.setMaxResults(16); recordServiceClient.records(req) .subscribe();
  57. 57. Messaging-Style static class TestHandler implements RSocketHandler { @MessageMapping("/canonical") Mono<String> handleCanonical(String payload) { return Mono.delay(Duration.ofMillis(10)) .map(l -> createResponse(payload)); } @MessageMapping("/async-arg") Mono<String> handleMonoArg(Mono<String> payload) { return payload.map(ServerResponderApp::createResponse); } }
  58. 58. Additional Protocol Features
  59. 59. Metadata and Data in Frames • Each Frame has an optional metadata payload • The metadata payload has a MIME-Type but is otherwise unstructured • Very flexible • Can be used to carry metadata about the data payload • Can be used to carry metadata in order to decode the payload • Generally means that payloads can be heterogenous and each message decoded uniquely
  60. 60. Fragmentation • Payload frames have no maximum size • The protocol is well suited to serving large payloads • Still Images (Facebook), Video (Netflix) • Both TCP MTUs and reliability on slow connections lead towards smaller payloads • Fragmentation provides a way to continue to reason about "logical frames" while ensuring that individual payloads are smaller • Applied transparently, after enforcement of RequestN semantics
  61. 61. Cancellation • All interaction types support cancellation • Cancellation is a signal by the requestor that any inflight processing should be terminated eagerly and aggressively • An obvious requirement for Request-Stream and Channel • But useful even in Request-Response where the response can be expensive to generate • Early termination can lead to significant improvement in efficiency
  62. 62. Leasing • Reactive back pressure ensures that a responder (or either party in a Channel) cannot overwhelm the receiver • This does not prevent a requestor from overwhelming a responder • This commonly happens in server-to-server environments where throughput is high • Leasing enables responders to signal capacity to requestors • This signal is useful for client-side load-balancing • Without preventing server-side load-balancing
  63. 63. RSocket • RSocket is a bi-directional, multiplexed, message-based, binary protocol • Utilizes Reactive Streams back pressure for efficiency and predictability • Provides primitives for the four common interaction models • Flexibility in transport, payload, language, and programming model • Let us know which programming model you prefer! • Myriad other features that make it great for modern application-to- application communication
  64. 64. > Stay Connected. https://projectreactor.io
 http://rsocket.io

×