-
-
Notifications
You must be signed in to change notification settings - Fork 761
gRPC Transport
Atmosphere 4.0 includes a gRPC transport module (atmosphere-grpc) that enables bidirectional streaming between clients and server over HTTP/2 using Protocol Buffers. It works standalone or alongside the servlet-based transports in Spring Boot.
gRPC Client AtmosphereGrpcServer
│ │
├── SUBSCRIBE /chat ────────────→│ AtmosphereGrpcService
│ │ ↓
│←── ACK (tracking_id) ─────────│ GrpcProcessor
│ │ ↓
├── MESSAGE "Hello" ────────────→│ GrpcHandler.onMessage()
│ │ ↓
│←── MESSAGE "Hello" ───────────│ Broadcaster.broadcast()
│ │
The gRPC transport maps onto Atmosphere's core abstractions: each gRPC stream becomes an AtmosphereResource, subscribes to a Broadcaster, and participates in the same pub/sub model as WebSocket and SSE clients.
<dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-grpc</artifactId> <version>4.0.4</version> </dependency>
public class ChatGrpcHandler extends GrpcHandlerAdapter { @Override public void onOpen(GrpcChannel channel) { log.info("gRPC client connected: {}", channel.uuid()); } @Override public void onMessage(GrpcChannel channel, String message) { log.info("gRPC message from {}: {}", channel.uuid(), message); } @Override public void onClose(GrpcChannel channel) { log.info("gRPC client disconnected: {}", channel.uuid()); } }
var framework = new AtmosphereFramework(); try (var server = AtmosphereGrpcServer.builder() .framework(framework) .port(9090) .handler(new ChatGrpcHandler()) .enableReflection(true) .build()) { server.start(); server.awaitTermination(); }
# List available services grpcurl -plaintext localhost:9090 list # Subscribe and send a message grpcurl -plaintext -d '{"type":"SUBSCRIBE","topic":"/chat"}' \ localhost:9090 org.atmosphere.grpc.AtmosphereService/Stream
The gRPC transport uses a single bidirectional streaming RPC:
syntax = "proto3"; package org.atmosphere.grpc; service AtmosphereService { rpc Stream(stream AtmosphereMessage) returns (stream AtmosphereMessage); } message AtmosphereMessage { MessageType type = 1; string topic = 2; string payload = 3; bytes binary_payload = 4; map<string, string> headers = 5; string tracking_id = 6; } enum MessageType { SUBSCRIBE = 0; UNSUBSCRIBE = 1; MESSAGE = 2; HEARTBEAT = 3; ACK = 4; ERROR = 5; }
The GrpcHandler interface defines lifecycle callbacks for gRPC connections:
| Callback | When Fired |
|---|---|
onOpen(GrpcChannel) |
Stream established |
onMessage(GrpcChannel, String) |
Text message received |
onBinaryMessage(GrpcChannel, byte[]) |
Binary message received |
onClose(GrpcChannel) |
Stream closed |
onError(GrpcChannel, Throwable) |
Error occurred |
Extend GrpcHandlerAdapter and override only the callbacks you need.
GrpcChannel wraps the gRPC StreamObserver and provides methods for sending messages:
channel.uuid(); // Connection tracking ID channel.write("Hello"); // Send text message channel.write(bytes); // Send binary message channel.write("/chat", "Hello"); // Send to specific topic channel.isOpen(); // Check connection state channel.resource(); // Associated AtmosphereResource channel.close(); // Close connection
AtmosphereGrpcServer.builder() .framework(framework) // AtmosphereFramework instance (required) .port(9090) // Server port (default: 9090) .handler(new MyHandler()) // GrpcHandler (default: GrpcHandlerAdapter) .enableReflection(true) // gRPC server reflection (default: true) .interceptor(myServerInterceptor) // Add gRPC ServerInterceptors .build();
The server implements AutoCloseable — use try-with-resources for automatic cleanup.
The Spring Boot starter auto-configures a gRPC server alongside the servlet container when atmosphere-grpc is on the classpath.
<dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-spring-boot-starter</artifactId> <version>4.0.4</version> </dependency> <dependency> <groupId>org.atmosphere</groupId> <artifactId>atmosphere-grpc</artifactId> <version>4.0.4</version> </dependency>
atmosphere: packages: com.example.chat grpc: enabled: true port: 9090 enable-reflection: true
| Property | Default | Description |
|---|---|---|
atmosphere.grpc.enabled |
false |
Enable gRPC transport server |
atmosphere.grpc.port |
9090 |
gRPC server port |
atmosphere.grpc.enable-reflection |
true |
Enable gRPC server reflection |
@Bean public GrpcHandler grpcHandler() { return new GrpcHandlerAdapter() { @Override public void onOpen(GrpcChannel channel) { log.info("gRPC client connected: {}", channel.uuid()); } @Override public void onMessage(GrpcChannel channel, String message) { log.info("gRPC message: {}", message); } }; }
If no GrpcHandler bean is defined, a default GrpcHandlerAdapter is used.
The gRPC server starts and stops with the Spring lifecycle automatically.
With both the servlet container and gRPC server running, clients can connect over any transport — WebSocket, SSE, long-polling via HTTP, or gRPC via HTTP/2 — and they all share the same Broadcasters:
@ManagedService(path = "/chat") public class Chat { @Message public String onMessage(String message) { // Broadcast reaches ALL clients — WebSocket, SSE, AND gRPC return message; } }
The Java client (wAsync) supports gRPC as a transport:
Client client = Client.newClient(); Request request = client.newRequestBuilder() .uri("grpc://localhost:9090/chat") .transport(Request.TRANSPORT.GRPC) .build(); Socket socket = client.create(); socket.on(Event.MESSAGE, msg -> System.out.println("Received: " + msg)) .open(request); socket.fire("Hello via gRPC!");
See the gRPC chat sample for a complete working example.
- Understanding Broadcaster — the pub/sub bus that gRPC connections participate in
- Java Client (wAsync) — async Java client with gRPC support
- Getting Started with Spring Boot — servlet-based transports