rgoussu@goussu: ~/library/java/protocols
~/library/java/protocols cat grpc.md

gRPC in Java

# Protobuf contracts, grpc-java, streaming modes and deadlines, with the Spring, Quarkus and Micronaut integrations and when to prefer gRPC over REST.

Conceptsaved 2026-08-09 #java#protocols#grpc#protobuf#frameworks

Overview

gRPC is contract-first binary RPC: services and messages are declared in protobuf .proto files, code is generated for every language, and calls run over HTTP/2 with multiplexing and native streaming. On the JVM the canonical runtime is grpc-java; the frameworks differ mainly in how they host it — Spring through a starter, Quarkus through a deeply integrated extension with reactive stubs, Micronaut through compile-time wiring. It shines for internal service-to-service traffic where latency, streaming and cross-language contracts matter; it is a poor fit for browsers, which is why gateways exist.

Key points

  • The contract is the .proto file: message and service definitions, evolved by field-number discipline — never reuse or renumber a field; add optional fields instead.
  • Codegen: protoc plus the grpc-java plugin, driven from the build — the protobuf Gradle plugin or the protobuf Maven plugin — producing message classes and stub variants (blocking, future, async).
  • grpc-java servers run on a Netty transport; an in-process transport makes tests fast and hermetic.
  • Four call shapes: unary, server-streaming, client-streaming, bidirectional — all first-class in the IDL, no polling or upgrade tricks.
  • Deadlines, not timeouts: the client attaches an absolute deadline that propagates across hops; servers should check Context cancellation. Metadata (headers/trailers) carries auth tokens and correlation ids; errors are Status codes + optional details.
  • Spring: the community starter (grpc-ecosystem's grpc-spring, formerly grpc-spring-boot-starter) and the newer official Spring gRPC project both wire @GrpcService-style beans into Boot.
  • Quarkus gRPC generates Mutiny stubs (Uni/Multi), supports serving gRPC on the main HTTP port, and registers clients via @GrpcClient.
  • Micronaut gRPC hosts BindableService beans on its Netty server with compile-time DI.
  • Browsers can't speak gRPC directly: use gRPC-Web, Envoy's gRPC-JSON transcoding, or the Connect protocol to expose a REST/JSON face.

Details

Build integration

The .proto files live in src/main/proto; the build invokes protoc with the grpc-java codegen plugin. Gradle uses the protobuf Gradle plugin (com.google.protobuf); Maven historically the xolstice protobuf-maven-plugin, with maintained forks and protoc-toolchain alternatives since. Generated sources are build output, not committed. Keep .proto files in a shared artifact or a schema repo when several services consume them — the contract's home matters more than the codegen mechanics.

Framework hosting compared

Concern Plain grpc-java Spring (Spring gRPC / grpc-spring) Quarkus gRPC Micronaut gRPC
Service declaration extend generated *ImplBase, register on ServerBuilder @GrpcService-annotated bean @GrpcService bean, Mutiny or *ImplBase BindableService bean
Stub style blocking / future / async StreamObserver same as grpc-java Mutiny Uni/Multi same as grpc-java
Client injection manual channel + stub @GrpcClient injection @GrpcClient injection DI-managed channels
Port model dedicated port dedicated port dedicated or unified with HTTP dedicated port
Extras interceptors health, reflection, security integration dev-mode reflection UI, TLS config service discovery hooks

Cross-cutting concerns (auth, logging, retries) belong in ServerInterceptor / ClientInterceptor — the filter model of gRPC — in every stack.

Streaming, deadlines, metadata

Server streaming suits feeds and large result sets; client streaming suits uploads and aggregation; bidirectional streams are effectively a typed message channel (and compete with WebSockets inside the data centre). Always set client deadlines — a gRPC call without a deadline is an unbounded resource hold — and propagate them; grpc-java carries them in Context. Retries belong only on idempotent methods, configured via service config, never blanket.

When gRPC over REST

  • Internal service mesh: east-west traffic where both ends are yours; meshes and load balancers understand HTTP/2 and gRPC health/metadata natively.
  • Streaming: any of the three streaming shapes; REST has no equivalent.
  • Polyglot contracts: one .proto generates consistent clients for Java, Go, Python… — stronger than sharing an OpenAPI file.
  • Latency/throughput: binary framing and multiplexing beat JSON parsing at volume.

Prefer REST for public APIs, browser clients, and anywhere human debuggability and ubiquitous tooling outweigh performance; expose gRPC services externally through transcoding rather than forcing gRPC on consumers.

Examples

service OrderService {
  rpc GetOrder (GetOrderRequest) returns (Order);
  rpc WatchOrders (WatchRequest) returns (stream OrderEvent); // server streaming
}
// Quarkus — Mutiny stub client with a deadline
@GrpcClient("orders")
OrderService client;

Uni<Order> order = client.getOrder(req)
    .ifNoItem().after(Duration.ofSeconds(2)).fail();

Related