Skip to main content

Command Palette

Search for a command to run...

Demystifying Remote Procedure Calls

Updated
โ€ข6 min read
Demystifying Remote Procedure Calls
R

SDE JL2 @A.P. Moller - Maersk Ex @Lummo | GSoC '23 Mentor @Jenkins | GSoC '22 @Keptn | Ex SWE Intern @redBus, @LocalStack & @Economize | LFX '21 @moja global | GSoD '21 @Wechaty | GSoD'20 @gRPC-Gateway | ICPC Regionalist 2020

I write software for a living, and I've primarily worked on "backend" systems & infrastructure. I currently work at A.P. Moller - Maersk. Interested in Algorithms, Distributed Systems, & Databases.

In the world of distributed systems and microservices, communication between different components is fundamental. A common pattern involves a client sending a request to a server, the server processing it, and sending back a response. While this request-response paradigm is universal, Remote Procedure Calls (RPCs) offer a specific and powerful way to structure this interaction.

Having been around for decades, RPCs have seen a significant revival, becoming a popular choice for inter-service communication, especially within microservice architectures. But what exactly are they, and why the resurgence?

What is RPC?

At its core, RPC is a communication protocol designed with a specific goal: to make a network call look and feel just like a regular, local function call to the developer.

Imagine you have a function calculate_power(base, exponent) defined locally. Calling it is simple: result = calculate_power(2, 3).

Now, imagine this calculate_power function actually resides on a different server across the network. How do you call it? Normally, you'd need to:

  1. Figure out the server's address and port.

  2. Establish a network connection (like TCP).

  3. Serialize your input data (base=2, exponent=3) into a format the server understands (like JSON or XML).

  4. Send the serialized data over the network.

  5. Wait for the server's response.

  6. Receive the response data.

  7. Deserialize the response data back into a usable format (e.g., the result).

  8. Handle potential network errors, retries, timeouts, etc.

RPC aims to hide all of this complexity. With an RPC framework, the developer might still write code that looks remarkably similar to the local call: result = remote_calculator_service.calculate_power(2, 3).

The RPC framework handles the underlying details: finding the server, serialization, network transmission, deserialization, and often even basic error handling like retries โ€“ abstracting away the "remote" nature of the call. This concept is often called Location Transparency.

How Do RPCs Achieve This Magic? Stubs and IDL

The magic behind RPC lies in abstraction, primarily handled by stubs (sometimes called proxies) and an Interface Definition Language (IDL).

  1. Interface Definition Language (IDL): You first define the contract of your remote service using an IDL. This specifies the available procedures (functions), their parameters, and their return types in a language-neutral format. A very popular example is Protocol Buffers (Protobuf), where you define services and messages in .proto files.

Example Concept (*.proto style):

    service NotificationService {
      rpc SendOtp (SendOtpRequest) returns (SendOtpResponse);
    }

    message SendOtpRequest {
      string email = 1;
    }

    message SendOtpResponse {
      bool success = 1;
      string message = 2;
    }
  1. Stub Generators: RPC frameworks provide tools (stub generators) that read your IDL file and automatically generate source code files (stubs) for both the client and server sides in your chosen programming languages (e.g., Go, Java, Python, C++).

  2. Stubs (Client-Side): When the client application calls a remote procedure (like notification_service.SendOtp(request)), it's actually calling a method in the auto-generated client stub. This stub:

    • Takes the native language arguments (e.g., a SendOtpRequest object).

    • Marshals (serializes) the arguments into the agreed-upon network format (often a compact binary format with Protobuf).

    • Handles sending the serialized data over the network to the correct server (using protocols like TCP, HTTP/2, etc.).

    • Waits for the response.

    • Unmarshals (deserializes) the network response back into a native language object (e.g., a SendOtpResponse object).

    • Returns the result to the client application.

  3. Stubs (Server-Side): The server also uses auto-generated code (the server stub or skeleton). This stub:

    • Listens for incoming network requests.

    • Receives the serialized request data.

    • Unmarshals it into native language objects.

    • Calls the actual implementation of the procedure written by the server developer (the business logic).

    • Takes the return value from the business logic.

    • Marshals the return value into the network format.

    • Sends the response back to the client stub over the network.

By using IDL and auto-generated stubs, RPC frameworks ensure that both client and server adhere to the same contract and handle the complexities of network communication and data conversion automatically.

Why Use Remote Procedure Calls? (The Advantages)

RPCs offer several compelling advantages:

  1. Developer Productivity: Making remote calls look local significantly simplifies development and boosts productivity. Developers focus more on business logic and less on network plumbing.

  2. Strong API Contracts: Using an IDL enforces a clear, strongly-typed contract between client and server, reducing integration errors.

  3. Performance: Many modern RPC frameworks (like gRPC, which often uses HTTP/2 and Protobuf) are highly optimized for performance through:

    • Efficient binary serialization (smaller payloads than JSON/XML).

    • Support for connection pooling and multiplexing (sending multiple requests/responses over a single connection).

    • Support for streaming (client-streaming, server-streaming, bidirectional-streaming).

    • Built-in compression.

  4. Multi-Language Support: Define your service once in an IDL, and generate client/server code for numerous languages, facilitating polyglot microservice environments.

  5. Abstraction of Mundane Tasks: Frameworks often handle concerns like basic failure detection, retries, payload conversion, and network protocol details.

  6. Auto-Generated Client Libraries: No need to manually write client libraries for consumers of your service; they can be generated directly from the IDL.

Potential Downsides (Concerns)

While powerful, RPCs aren't without challenges:

  1. Network Latency: Despite the abstraction, it's still a network call, which is inherently orders of magnitude slower and less reliable than a true local function call. Developers must remain aware of this potential performance bottleneck and failure mode.

  2. Tight Coupling & Stub Regeneration: Changes to the service interface (defined in the IDL) require regenerating and redeploying both client and server stubs, which can introduce coupling. Careful API versioning is crucial.

  3. Testing: Testing RPC interactions can sometimes be less straightforward than testing simpler REST APIs, often requiring specific mocking or integration testing setups.

  4. Getting Started: There's an initial setup overhead involving choosing a framework, defining the IDL, and integrating the stub generation process into the build pipeline.

  5. Browser Support: Direct RPC (especially binary protocols like gRPC's default) from web browsers has historically been limited, although solutions like gRPC-Web act as a bridge (often translating to HTTP/1.1).

Conclusion

Remote Procedure Calls provide a highly effective abstraction for building distributed systems, particularly microservices. By making network communication resemble local function calls, they enhance developer productivity and enable efficient, strongly-typed interactions between services, even across different programming languages. While they come with considerations around network latency and the need for careful contract management, the benefits of abstraction and performance offered by modern frameworks like gRPC often make RPCs an excellent choice for internal service-to-service communication.

Thank you ๐Ÿ˜Š for taking the time โฐ to read this blog post ๐Ÿ“–. I hope you found the information ๐Ÿ“š helpful and informative ๐Ÿง . If you have any questions โ“ or comments ๐Ÿ’ฌ, please feel free to leave them below โฌ‡๏ธ. Your feedback ๐Ÿ“ is always appreciated.

Portfolio GitHub LinkedIn X