6.0 KiB
6.0 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| backend-grpc-streaming-deep | gRPC Streaming — bidirectional / server / client | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
gRPC Streaming Deep
gRPC 의 큰 차별점. 4 mode: unary, server stream, client stream, bidi. HTTP/2 + Protobuf.
📖 핵심 개념
- HTTP/2 multiplexing.
- Protobuf binary (작은).
- Type-safe (proto → 매 언어).
- Bidi 가 WebSocket 식.
💻 코드 패턴
Proto definition
syntax = 'proto3';
service ChatService {
rpc Send(Message) returns (Ack); // unary
rpc Subscribe(Filter) returns (stream Message); // server stream
rpc Upload(stream Chunk) returns (FileInfo); // client stream
rpc Chat(stream Message) returns (stream Message); // bidi
}
Server (TS, Connect-RPC)
import { ConnectRouter } from '@connectrpc/connect';
import { ChatService } from './gen/chat_connect';
export default (router: ConnectRouter) =>
router.service(ChatService, {
async send(req) {
return { ack: true };
},
async *subscribe(req) {
for (let i = 0; i < 100; i++) {
yield { text: `msg ${i}` };
await sleep(1000);
}
},
async upload(req) {
let total = 0;
for await (const chunk of req) {
total += chunk.data.length;
}
return { size: total };
},
async *chat(req) {
for await (const msg of req) {
yield { text: 'echo: ' + msg.text };
}
},
});
Client (TS)
import { createPromiseClient } from '@connectrpc/connect';
const client = createPromiseClient(ChatService, transport);
// Server stream
for await (const msg of client.subscribe({ topic: 'news' })) {
console.log(msg.text);
}
// Client stream
async function* chunks() {
for (const chunk of file) yield { data: chunk };
}
const result = await client.upload(chunks());
Connect-RPC (modern)
gRPC + gRPC-Web + Connect protocol 동시.
- Browser 에서 직접 (no envoy).
- HTTP/1.1 + HTTP/2 둘 다.
- TS-friendly.
→ gRPC 의 modern.
Buf (toolchain)
# buf.gen.yaml
version: v2
plugins:
- remote: buf.build/connectrpc/es
out: gen
- remote: buf.build/bufbuild/es
out: gen
buf generate
buf lint
buf breaking --against '.git#branch=main'
→ gRPC 의 npm.
Bidi streaming
// Server
async *chat(reqs) {
for await (const msg of reqs) {
// 매 client message 받음.
yield { reply: process(msg) };
}
}
// Client
async function* messages() {
yield { text: 'hi' };
await sleep(1000);
yield { text: 'how are you?' };
}
for await (const reply of client.chat(messages())) {
console.log(reply);
}
→ Real-time bidirectional. Chat / game.
Server stream use case
- Server-sent updates.
- Live data feed.
- Search results (incremental).
- Log streaming.
Client stream use case
- Large upload (chunks).
- Sensor data submission.
- Telemetry batch.
Cancellation
const ac = new AbortController();
setTimeout(() => ac.abort(), 5000);
for await (const msg of client.subscribe({}, { signal: ac.signal })) {
// ...
}
→ Stream 중단.
Error handling
try {
for await (const msg of stream) {
// ...
}
} catch (e) {
if (e.code === Code.Cancelled) ...;
if (e.code === Code.DeadlineExceeded) ...;
}
Deadline
const r = await client.send(req, { timeoutMs: 5000 });
Interceptor (middleware)
const authInterceptor: Interceptor = (next) => async (req) => {
req.header.set('Authorization', `Bearer ${token}`);
return next(req);
};
const transport = createConnectTransport({
baseUrl: '...',
interceptors: [authInterceptor],
});
vs REST
REST:
- HTTP/1.1.
- JSON (verbose).
- Stateless.
- 큰 ecosystem.
gRPC:
- HTTP/2 multiplexing.
- Protobuf (작은).
- Streaming native.
- Type-safe (codegen).
→ Internal microservice = gRPC.
Public API / web = REST / GraphQL.
vs WebSocket
WebSocket: bidi 만, schema 없음.
gRPC bidi: typed schema + RPC.
→ gRPC 가 strict schema. WS 가 free-form.
vs GraphQL Subscription
GraphQL Subscription: query 식 + WS.
gRPC streaming: RPC 식 + HTTP/2.
→ Different model. 같은 use case 가 가능.
Performance
gRPC 가 REST 보다 7-10x 빠름 (binary + multiplex).
Streaming = 큰 throughput (multiple parallel).
HTTP/2 head-of-line blocking 가 약간 (TCP level).
→ Internal high-performance.
gRPC-Web
Browser 가 native gRPC 안 됨.
- gRPC-Web (Envoy 가 transcode).
- 또는 Connect (native).
→ Connect 가 modern (envoy 없음).
Production
- Envoy (proxy).
- gRPC LB (HTTP/2 LB).
- Auth interceptor.
- Tracing (OpenTelemetry).
- Monitoring (latency p99, error rate).
Common pitfall
- gRPC LB 가 HTTP/2 aware 안 = sticky.
- Stream 의 backpressure 무시.
- Deadline 없음 = hang.
- Compression 가 default off (큰 payload 가 비효율).
- Schema breaking change.
🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| Internal microservice | gRPC |
| Streaming | gRPC bidi |
| Web client | Connect / gRPC-Web |
| Public API | REST + OpenAPI |
| Real-time chat | gRPC bidi 또는 WS |
| File upload | gRPC client stream |
❌ 안티패턴
- gRPC + REST 둘 다 같은 service: complexity.
- No deadline: hang.
- gRPC + non-HTTP/2 LB: sticky.
- 모든 거 unary (streaming 안 사용): gRPC 의 가치 ↓.
- Schema breaking change: client 깨짐.
🤖 LLM 활용 힌트
- Connect-RPC 가 modern (browser-friendly).
- Buf 가 toolchain.
- 4 mode (unary, server, client, bidi).
- Internal microservice 의 default.