gRPC Bidirectional Streaming — Full-Duplex Real-Time Communication
In this tutorial, you will learn about grpc bidirectional streaming. We cover key concepts, practical examples, and best practices to help you master this topic.
Bidirectional streaming enables both client and server to send messages simultaneously over a single gRPC stream — the most powerful and complex gRPC pattern.
Bidirectional Definition
service ThreatMonitor {
rpc MonitorThreats(stream ThreatQuery) returns (stream ThreatAlert);
}
Server Implementation
class ThreatMonitorServicer(threat_pb2_grpc.ThreatMonitorServicer):
async def MonitorThreats(self, request_iterator, context):
async def send_alerts():
async for alert in alert_stream():
if not context.is_active():
break
yield alert
async def handle_queries():
async for query in request_iterator:
print(f"Client watching: {query.severity_filter}")
# Update alert filter based on client query
# Run both concurrently
await asyncio.gather(
send_alerts(),
handle_queries(),
)
Client Implementation
async def run():
channel = grpc.aio.insecure_channel("localhost:50051")
stub = threat_pb2_grpc.ThreatMonitorStub(channel)
async def send_queries():
queries = [
threat_pb2.ThreatQuery(severity_filter="CRITICAL"),
threat_pb2.ThreatQuery(severity_filter="ALL"),
]
for query in queries:
yield query
await asyncio.sleep(30)
async def receive_alerts():
async for alert in stub.MonitorThreats(send_queries()):
print(f"ALERT: {alert.message} ({alert.severity})")
await receive_alerts()
Common Mistakes
1. Assuming Sequential Message Order
In bidirectional streams, messages from both sides can interleave. Design your protocol to handle out-of-order messages.
2. Not Handling Concurrent Writes
Both sides can write simultaneously. Use proper synchronization (async locks) if shared state is involved.
3. Blocking the Event Loop
Bidirectional streams should use async/await. Blocking calls in sync handlers block the entire channel.
4. No Keepalive Pings
Idle bidirectional streams may be dropped by proxies. Configure keepalive pings to maintain the connection.
5. Ignoring context.is_active()
Always check if the context is still active before processing. The client may disconnect at any time.
Practice Questions
- What makes bidirectional streaming different from server streaming?
- How do you implement bidirectional streaming with async?
- When should you use bidirectional over other streaming types?
- How do you handle concurrent sends and receives?
- What is the keepalive configuration for bidirectional streams?
Answers:
- Both sides send multiple messages independently. Server streaming has one client request and many server responses.
- Use
grpc.aio(async IO) in Python. Define async generators for both sending and receiving. - For real-time chat, live collaborative editing, gaming, and any application where both sides need to exchange messages asynchronously.
- Use async generators and asyncio.gather to run reading and writing concurrently. Never block on writes while reading.
- Set
grpc.keepalive_time_ms,grpc.keepalive_timeout_ms, andgrpc.keepalive_permit_without_callson both client and server.
Mini Project
Build a bidirectional streaming service for DodaTech's real-time threat monitoring. Clients connect, subscribe with severity filters, and receive alerts. Clients can change their filter mid-stream. Include keepalive and error handling.
What's Next
| Topic | Description |
|---|---|
| Channels | Connection management |
| Interceptors | Middleware for gRPC |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro