Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Multi-service reactive streams using Spring, Reactor, RSocket

329 vues

Publié le

Delivered at Oracle CodeOne

Publié dans : Logiciels
  • Soyez le premier à commenter

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

×