84 lines
3.6 KiB
Markdown
84 lines
3.6 KiB
Markdown
# 1. The same use case, three ways
|
|
|
|
Next: [2. How they measure](02-benchmarks.md)
|
|
|
|
---
|
|
|
|
One application in this module serves the same two operations over gRPC, RSocket and a raw
|
|
WebSocket: fetch one quote, and stream N quotes. Same JVM, same heap, same
|
|
[`QuoteSource`](../src/main/java/com/ankurm/protocols/QuoteSource.java), same five fields.
|
|
|
|
That constraint is the point. Cross-protocol benchmarks published on the internet almost always
|
|
compare three different applications, and end up measuring three different serialisation
|
|
libraries, three JIT states and three thread pools.
|
|
|
|
## What each one is, in one sentence
|
|
|
|
| | gRPC | RSocket | Raw WebSocket |
|
|
|---|---|---|---|
|
|
| Transport | HTTP/2 | TCP, WebSocket, or others | HTTP/1.1 Upgrade |
|
|
| Schema | `.proto`, mandatory, code-generated | none required | none |
|
|
| Encoding here | protobuf | CBOR (Spring's default) | JSON |
|
|
| Interaction models | 4, declared in the `.proto` | 4, chosen by return type | whatever you invent |
|
|
| Demand signalling | none in the API | `request(n)` on the wire | none at all |
|
|
| Deadlines | per call, absolute, propagating | none built in | none |
|
|
| Browser client | needs grpc-web + a proxy | yes, over the WebSocket transport | yes, natively |
|
|
| Boot 4 starters | `spring-boot-starter-grpc-server` / `-client` | `spring-boot-starter-rsocket` | `spring-boot-starter-websocket` |
|
|
|
|
## The three server sides, side by side
|
|
|
|
**gRPC** ([`GrpcQuoteService`](../src/main/java/com/ankurm/protocols/GrpcQuoteService.java)) is
|
|
generated-class inheritance. There is no registration code: the generated `ImplBase` is a
|
|
`BindableService` and Boot registers every such bean.
|
|
|
|
```java
|
|
@Override
|
|
public void getQuote(QuoteRequest request, StreamObserver<Quote> observer) {
|
|
observer.onNext(toProto(source.at(request.getSymbol(), 0)));
|
|
observer.onCompleted();
|
|
}
|
|
```
|
|
|
|
**RSocket** ([`RSocketQuoteController`](../src/main/java/com/ankurm/protocols/RSocketQuoteController.java))
|
|
is annotation routing, and the interaction model *is the return type*:
|
|
|
|
```java
|
|
@MessageMapping("quote")
|
|
public Mono<Quote> quote(String symbol) { ... } // request/response
|
|
|
|
@MessageMapping("quotes")
|
|
public Flux<Quote> quotes(StreamSpec spec) { ... } // request/stream
|
|
```
|
|
|
|
**Raw WebSocket** ([`WebSocketQuoteHandler`](../src/main/java/com/ankurm/protocols/WebSocketQuoteHandler.java))
|
|
is a `switch` on a string, because there is nothing else:
|
|
|
|
```java
|
|
switch (parts[0]) {
|
|
case "QUOTE" -> send(session, source.at(parts[1], 0));
|
|
case "STREAM" -> { for (int i = 0; i < count; i++) { send(...); } }
|
|
...
|
|
}
|
|
```
|
|
|
|
That third block is the honest summary of what a raw WebSocket gives you: a bidirectional pipe
|
|
for text or bytes. Everything above it — routing, correlation, errors, versioning, demand
|
|
— is yours to invent, and inventing it is how teams end up with a private, undocumented
|
|
protocol that only its authors can debug. STOMP exists to stop that, and is covered in
|
|
[`../sse-websocket`](../../sse-websocket/README.md).
|
|
|
|
## Boot 4 version notes
|
|
|
|
Boot 4.1.1's BOM pins **grpc-java 1.83.1** and **protobuf-java 4.35.1**, both deliberately older
|
|
than the newest releases on Maven Central. Overriding them independently is how you get a
|
|
`NoSuchMethodError` between grpc-java and its shaded Netty. Note that these have moved since Boot
|
|
4.1.0, which pinned 1.80.0 and 4.34.2 — so a `spring.grpc.*` guide written against 4.1.0 is
|
|
already describing different jars.
|
|
|
|
RSocket is **rsocket-java 1.1.5**, and `spring-boot-starter-rsocket` brings
|
|
`jackson-dataformat-cbor` on purpose: CBOR, not JSON, is Spring's default RSocket data mime type.
|
|
|
|
---
|
|
|
|
Next: [2. How they measure](02-benchmarks.md)
|