← Home
🏗Crack System Design with DSA AnimatorSystem DesignNEW
🏗 System Design›Networking & Realtime

🚀gRPC

High-performance RPC over HTTP/2 with strongly typed Protobuf contracts.

📌 Key IdeagRPC is ideal for internal service-to-service communication where low latency, strict contracts, and streaming matter. Core production concerns are schema evolution, deadlines/timeouts, retries, observability, and secure transport.
📖

Definition

gRPC is a Remote Procedure Call framework using HTTP/2 transport and Protocol Buffers (protobuf) for interface definition and payload serialization. Clients and servers are generated from .proto contracts.

✨

Why It Matters

  • Compact binary payloads reduce network overhead vs JSON in many workloads.
  • IDL-first design enforces type-safe API contracts across teams/languages.
  • Built-in streaming modes support real-time bidirectional communication.
  • HTTP/2 multiplexing improves connection utilization for high-QPS systems.
✅

When to Use

  • Internal microservices with strict latency and type-safety requirements.
  • Multi-language backend platforms needing shared IDL contracts.
  • Workloads requiring streaming communication patterns.
🚫

When NOT to Use

  • Public browser-heavy APIs without gateway/transcoding plan.
  • Teams without protobuf/versioning discipline or observability maturity.
💻

Code Variations

1️⃣ Protobuf contractprotobuf
syntax = "proto3";

package users.v1;

service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  rpc StreamUsers(StreamUsersRequest) returns (stream UserEvent);
}

message GetUserRequest { string user_id = 1; }
message GetUserResponse { string user_id = 1; string name = 2; string email = 3; }
message StreamUsersRequest { string tenant_id = 1; }
message UserEvent { string user_id = 1; string action = 2; int64 ts_epoch_ms = 3; }
Never reuse field numbers; reserve removed fields to avoid wire compatibility bugs.
2️⃣ Server implementation (unary + deadline awareness)java
@GrpcService
public class UserGrpcService extends UserServiceGrpc.UserServiceImplBase {
  @Override
  public void getUser(GetUserRequest req, StreamObserver<GetUserResponse> obs) {
    try {
      User u = userRepo.load(req.getUserId());
      obs.onNext(GetUserResponse.newBuilder()
          .setUserId(u.id()).setName(u.name()).setEmail(u.email()).build());
      obs.onCompleted();
    } catch (NotFoundException e) {
      obs.onError(Status.NOT_FOUND
          .withDescription("user not found")
          .asRuntimeException());
    }
  }
}
3️⃣ Client call with deadline + metadatajava
Metadata md = new Metadata();
md.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER),
      "Bearer " + token);

UserServiceGrpc.UserServiceBlockingStub stub =
    MetadataUtils.attachHeaders(baseStub, md)
        .withDeadlineAfter(300, TimeUnit.MILLISECONDS);

GetUserResponse res = stub.getUser(
    GetUserRequest.newBuilder().setUserId("u-123").build()
);
Always set deadlines; otherwise hung dependencies can consume threads indefinitely.
4️⃣ Status code handling guidelinetext
NOT_FOUND       -> entity missing
INVALID_ARGUMENT -> validation failed
UNAUTHENTICATED  -> auth missing/invalid
PERMISSION_DENIED-> auth valid but forbidden
UNAVAILABLE      -> transient infra/network issue
DEADLINE_EXCEEDED-> timeout
📊

gRPC capabilities and design decisions

AspectWhat gRPC givesWhat you must design carefully
ContractsStrong .proto schema + code generationBackward-compatible schema evolution rules
PerformanceProtobuf + HTTP/2 efficiencyTune deadlines, connection pools, message sizes
StreamingUnary + server/client/bidi streamingFlow control and backpressure handling
ReliabilityRich status model + retry supportIdempotency + retry policy boundaries
SecurityTLS/mTLS + metadata auth headersAuthn/z propagation and secret rotation
🔀

Common Confusions Cleared Up

gRPC vs REST boundary

gRPC often excels inside trusted service mesh. REST still dominates external/public APIs due to browser/tooling interoperability. Many systems run hybrid: REST at edge, gRPC internally.

Retries are not always safe

Only retry idempotent operations automatically. Non-idempotent writes need idempotency keys/transaction semantics before enabling aggressive retry policies.

Schema evolution is a governance problem

Backward compatibility requires discipline: do not change field types casually, do not reuse tag numbers, and phase-out fields with reserve/deprecation strategy.

🧪

Variations / Implementations

Unary RPC

Single request -> single response. Most common pattern for internal synchronous calls.

Server streaming

One request -> many responses. Useful for large result sets, live feeds, incremental updates.

Client streaming

Many requests -> one response. Useful for batch ingestion/aggregation.

Bidirectional streaming

Both sides stream concurrently. Useful for chat, collaborative sessions, telemetry pipelines.

⚠️

Common Mistakes / Anti-Patterns

  • No deadlines on client calls.
  • Mapping all errors to INTERNAL (poor debuggability).
  • Breaking protobuf compatibility during schema changes.
  • Retrying non-idempotent writes blindly.
  • Skipping mTLS/auth metadata in internal zero-trust environments.
🎤

Interview Q&A

🧾

Quick Cheat Sheet

TransportHTTP/2 (multiplexed) + protobuf binary payload.
Contract source.proto IDL with generated clients/servers.
Call typesUnary, server-stream, client-stream, bidi-stream.
Golden rulesSet deadlines, keep schema backward-compatible, retry safely.
🔗

Related Topics