1
0

Add the protocol-comparison module

This commit is contained in:
2026-09-04 00:52:18 +05:30
parent 5224afdad2
commit d56c60824e
32 changed files with 1710 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
# 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 &mdash; routing, correlation, errors, versioning, demand
&mdash; 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 &mdash; 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)