RPC

RPC — Remote Procedure Call

RPC (Remote Procedure Call) is a communication style where a client invokes a server operation as if it were a local function call. The RPC framework handles serialization, network transport, and deserialization, hiding the network boundary from the caller. You define a service interface; the framework generates client stubs that make remote calls look like local method calls.

RPC is the foundation of gRPC (Google's modern RPC framework) and was the basis of SOAP/WCF. It contrasts with REST, which models resources and uses HTTP verbs rather than procedure calls.

How RPC Works

Client                          Server
──────                          ──────
OrderService.PlaceOrder(req)
  → Serialize req to bytes
  → Send over TCP/HTTP
                                → Deserialize bytes to req
                                → Execute PlaceOrder(req)
                                → Serialize result to bytes
                                → Send response
  ← Deserialize bytes to result
  ← Return result to caller

The client stub makes the call look local. The network, serialization, and error handling are handled by the framework.

RPC vs REST

Dimension RPC (gRPC) REST
Interface style Procedure/action-oriented (PlaceOrder, GetUser) Resource-oriented (POST /orders, GET /users/1)
Contract Strongly typed (Protobuf IDL, WSDL) Loosely typed (OpenAPI, convention)
Serialization Binary (Protobuf) — compact, fast JSON/XML — human-readable, larger
Transport HTTP/2 (gRPC) HTTP/1.1 or HTTP/2
Streaming Native bidirectional streaming (gRPC) Limited (SSE, WebSocket for push)
Browser support Limited (requires gRPC-Web proxy) Native
Best for Internal service-to-service, high-throughput, streaming Public APIs, browser clients, simple CRUD

The Fallacies of Distributed Computing

RPC's "local call" abstraction is leaky. The eight fallacies of distributed computing (Peter Deutsch) describe what RPC hides:

  1. The network is reliable — it isn't. Calls can fail, time out, or be delivered twice.
  2. Latency is zero — network calls are 100–1000× slower than local calls.
  3. Bandwidth is infinite — serialization and payload size matter.
  4. The network is secure — calls can be intercepted or replayed.

Practical implication: always handle RPC failures explicitly. Implement retries with idempotency keys, timeouts, and circuit breakers. Never assume a failed RPC means the server didn't execute the operation — it may have executed and the response was lost.

Pitfalls

Versioning hell
Changing a Protobuf message (removing a field, reusing a field number) breaks existing clients. Mitigation: never remove or reuse field numbers; use the reserved keyword for deprecated fields; version service names (OrderServiceV2) for breaking changes.

Tight coupling through generated stubs
Client and server must share the same .proto file. A server-side change requires regenerating and redeploying all clients. Treat .proto files as a public API contract with the same backward-compatibility rules as a REST API.

Serialization overhead masking latency
Protobuf is fast, but large messages (nested objects, repeated fields) still add serialization cost. A 10KB Protobuf message serialized 10,000 times/sec adds measurable CPU. Profile serialization in hot paths; use streaming for large payloads instead of single large messages.

gRPC C# Example

// Server implementation
public class OrderServiceImpl : OrderService.OrderServiceBase
{
    public override Task<OrderResponse> PlaceOrder(
        OrderRequest request, ServerCallContext context)
    {
        // context.CancellationToken respects client-side deadlines
        return Task.FromResult(new OrderResponse
        {
            OrderId = Guid.NewGuid().ToString(),
            Status = "Accepted"
        });
    }
}

// Client call with deadline
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new OrderService.OrderServiceClient(channel);
var response = await client.PlaceOrderAsync(
    new OrderRequest { ProductId = "sku-123", Quantity = 2 },
    deadline: DateTime.UtcNow.AddSeconds(5)); // client-side timeout

Questions

References


Whats next