Medium system-design api

How does gRPC work, and what are the four RPC types?

The 30 second answer

gRPC is a remote procedure call framework. You declare messages and services in a proto file, generate stubs, and call methods instead of hand-writing URLs. The default stack is HTTP/2 for transport and Protocol Buffers for the contract. There are four method kinds: unary, server streaming, client streaming and bidirectional streaming. I always set a deadline, because there is no default, and I treat the result as a gRPC status, not as an HTTP status.

gRPC is a remote procedure call framework. A service is defined as a set of named methods, each with a request and response type. The default stack is HTTP/2 for transport and Protocol Buffers (protobuf) for the contract and payload. Other encodings exist, but protobuf is the one the official documentation assumes.

HTTP/2 multiplexes many concurrent streams over one connection, with binary framing and header compression. gRPC maps each RPC to an HTTP/2 stream. That is why a single long-lived connection can carry many in-flight calls, and why connection-level load balancing is a common operational pitfall, covered in the next question.

You declare messages and services in a .proto file and run protoc, with a gRPC plugin, to generate stubs and skeletons. Field numbers identify fields on the wire; they are not decorative and must not be changed once a type is in use. In Java this is commonly wired through a gRPC server or a Spring Boot application that hosts generated services. Callers typically call a generated method rather than hand-writing URL paths or JSON field names.

gRPC defines four kinds of method. Message order is preserved within an individual RPC.

  1. Unary. The client sends one request and receives one response: rpc SayHello(HelloRequest) returns (HelloResponse);
  2. Server streaming. The client sends one request and reads a sequence of responses until the stream ends: rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
  3. Client streaming. The client writes a sequence of messages, then waits for a single response: rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
  4. Bidirectional streaming. Both sides send a sequence of messages on a read-write stream. The two streams are independent: the server may wait for all client messages, or interleave reads and writes.
syntax = "proto3";

package catalog.v1;

service Catalog {
  rpc GetOffer(GetOfferRequest) returns (Offer);
  rpc WatchPrices(WatchPricesRequest) returns (stream PriceTick);
}

message GetOfferRequest {
  string offer_id = 1;
}

message Offer {
  string id = 1;
  string title = 2;
  Money price = 3;
}

message Money {
  string currency_code = 1;
  string decimal_amount = 2;
}

message WatchPricesRequest {
  repeated string offer_id = 1;
}

message PriceTick {
  string offer_id = 1;
  Money price = 2;
}

Deadlines and cancellation. Clients should set a deadline (a point in time) or a timeout (a duration, converted to a deadline when the call starts). If the deadline passes, the client fails the RPC with DEADLINE_EXCEEDED. By default gRPC does not set a deadline, so a client can wait indefinitely; official guidance is to always set a realistic one. On the server, once the client's deadline has passed, gRPC cancels the call (CANCELLED). The application is still responsible for stopping work it spawned. When a server makes a downstream RPC, some implementations, including Java and Go by default, propagate the remaining deadline. Either side may cancel an RPC at any time.

Every RPC returns a status: an integer code plus a description. These are not HTTP status codes. Applications should use only the defined codes. A unary success is a generated message with status OK (0). A missing offer is NOT_FOUND (5), not an HTTP 404 at the application layer. Other codes interviewers expect include INVALID_ARGUMENT (3), DEADLINE_EXCEEDED (4), PERMISSION_DENIED (7), UNAVAILABLE (14) and UNAUTHENTICATED (16). UNAVAILABLE is the usual signal that a retry with backoff may be appropriate; it is not always safe to retry a non-idempotent RPC.

Alternate interview scenarios

"Walk me through a streaming search API."

I would use server streaming: the client sends one query and reads hits as they become available, which avoids waiting to buffer the whole page. If both sides need to talk at once, for example a session that refines the query while results still arrive, I would use bidirectional streaming. I would still set a deadline on the call, and I would be ready to cancel if the user walks away.

"What happens if you forget to set a timeout on a gRPC call?"

There is no default deadline, so the client can wait indefinitely. I treat that the same way I treat a missing HTTP client timeout: it is a production defect. I set a realistic deadline and make sure the server stops work when the call is cancelled.

Sources (4)
  1. gRPC core conceptsgrpc.io
  2. Protocol Buffers proto3 language guideprotobuf.dev
  3. gRPC deadlinesgrpc.io
  4. gRPC status codesgrpc.io