Understanding RPC: Principles, Challenges, and High‑Availability Design
This article explains the concept of Remote Procedure Call (RPC), compares it with other inter‑system communication methods, outlines the three core problems RPC must solve—call ID mapping, serialization, and network transport—and discusses high‑availability considerations and I/O models for building robust RPC frameworks.
Simple Explanation
RPC allows a program on one machine (client) to invoke a function or method on another machine (server) and receive a result, hiding the underlying communication details.
It abstracts away socket or HTTP handling, making remote calls look like local function calls.
Why Use RPC?
RPC aims to make building distributed applications easier by providing powerful remote invocation while preserving the simplicity of local call semantics, offering a transparent mechanism so developers do not need to distinguish between local and remote calls.
Three Problems RPC Must Solve
To achieve seamless remote calls, RPC must address:
Call ID Mapping : Each function must have a unique ID shared across processes because function pointers cannot be used across address spaces. Clients attach this ID to requests, and both client and server maintain a {function ↔ Call ID} table.
Serialization and Deserialization : Parameters must be converted to a byte stream for transmission and reconstructed on the server side, handling language differences and process isolation.
Network Transmission : RPC relies on a network layer (commonly TCP, but UDP or HTTP/2 can also be used) to transport the Call ID and serialized data, requiring a protocol that defines headers and message bodies.
Considerations for Building a Highly Available RPC Framework
Service registration and discovery (e.g., using Zookeeper) to locate multiple instances of a service.
Load‑balancing strategies to select an instance.
Caching of service lists to avoid frequent registry lookups.
Support for asynchronous calls.
Version control for backward‑compatible interface changes.
Thread‑pool management to avoid creating a new thread per request.
A Complete RPC Call Flow
Network communication is the first step; most RPC frameworks use TCP for reliable transmission.
Because arguments are objects, they must be serialized into binary data before being sent, and the server must deserialize them back into objects.
Data is often split into multiple packets, leading to issues like packet sticking and half‑packet problems; a defined RPC protocol (header + body) resolves this.
Client serializes request object and sends it over TCP.
Server receives binary data from the TCP channel.
Server parses the protocol, deserializes the request, locates the implementation, and invokes the method.
Server serializes the result and writes it back to the TCP channel.
Client receives the response packet and deserializes it, completing the call.
Improving Network Communication Performance
Choosing an efficient I/O model is key to RPC performance. The five classic I/O models are:
Synchronous blocking I/O (BIO)
Synchronous non‑blocking I/O
I/O multiplexing (NIO)
Signal‑driven I/O
Asynchronous I/O (AIO)
In practice, BIO and NIO are most common; NIO improves thread utilization by separating connection handling from data read/write, often using the Reactor pattern.
Modern RPC frameworks adopt I/O multiplexing (e.g., select, poll, epoll) and many rely on Java’s Netty, which implements the Reactor model with various thread‑model options (single‑thread, multi‑thread, master‑worker).
Top Architecture Tech Stack
Sharing Java and Python tech insights, with occasional practical development tool tips.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.