RPC; Everything Old is New Again
Possibly since the inception of computing, the paradigm of client to server communication has been an important, sometimes overlooked requirement for distributed computing. From a developer perspective, it doesn’t feel productive to be concerned with the details of how the communication occurs, but rather spend your time on of the business rules of the software you are working on.
These are the factors that created a need for the Remote Procedure Call pattern. Remote Procedure Call (RPC) is a protocol that one program can use to request a service from a program located in another computer on a network without having to understand the network’s details. RPC is used to communicate between a client and server. The client makes a procedure call that appears to be local but is actually run on a remote server.
Here’s how RPC works:
- Procedure Call: A client calls a procedure on a server as if it were calling a local procedure or function. The client needs to know the identifier of the remote procedure, as well as the server’s address.
- Message Passing: The RPC protocol translates (or “marshals”) the procedure call into a message that it sends to the remote server. This message includes the name or identifier of the procedure and the parameters to be passed to it.
- Procedure Execution: The server receives the message, translates it back into a procedure call, and executes the procedure using the provided parameters.
- Return of Results: After the procedure is executed, the server sends the result back to the client. The RPC protocol translates this into a return from a procedure call.
The process is designed to be transparent to the client; from the client’s perspective, the procedure call and response appear to be local, even though they are actually happening on a remote server. The goal of RPC is to simplify the process of creating distributed and networked applications by abstracting the complexities of network communication.
The history of RPC pattern in software development goes back several decades:
- Early 1970s: The concepts that form the basis of RPCs were first developed in the early 1970s. At this time, computer scientists were working on ways to distribute computing tasks across multiple machines. While it wasn’t yet termed “RPC,” the idea of a program on one machine calling a procedure on another was being explored.
- 1980s: The term “Remote Procedure Call” (RPC) started to become popular in the 1980s. The Xerox Palo Alto Research Center (PARC) was instrumental in developing the concept of RPCs as part of the Xerox Network Systems (XNS) protocol suite. It was designed to be a simple way to make a call on a remote machine, and XNS RPC was the first protocol designed specifically for RPCs. Also during this period, Sun Microsystems released its Network File System (NFS) using RPC to allow clients to access files over the network as if they were on the local machine.
- 1990s: In the 1990s, RPC started to become widely used in enterprise applications. Technologies like DCE/RPC (Distributed Computing Environment / Remote Procedure Calls), Microsoft’s MS-RPC, and ONC RPC (Open Network Computing Remote Procedure Call) emerged during this time.
- 2000s: The 2000s saw a shift toward the use of web-based RPC technologies, often taking advantage of HTTP as the transport layer. The most significant of these was probably SOAP (Simple Object Access Protocol), which allowed for RPC communication using XML over HTTP. XML-RPC also emerged as a simpler alternative to SOAP.
- 2010s: While RPC-style interactions remained popular, there was also a growing trend towards Representational State Transfer (REST) and RESTful APIs during this decade. JSON-RPC and gRPC (Google’s high performance, open-source RPC framework) also appeared during this period. gRPC in particular became quite popular due to its performance benefits and wide language support.
- 2020s: By this time, RPC has become a core part of many applications, both large and small. While RESTful APIs often dominate the web development landscape, RPC-style interactions are still commonly used, especially in performance-critical applications or microservices architectures.
RPC is most effectively implemented in small applications where a single developer, or a single team are responsible for implementing both the server and client logic, and where it is advantageous to abstract the communication layer such that it ‘feels’ to the developer like they are simply making a function call and getting results locally. If you are responsible for implementing the procedure (function) as well as doing something with the results, using RPC may be an extremely productive pattern to follow.
With the popularity of this pattern returning after falling out of favor for more loosely coupled REST patterns the past decade or so, RPC has come back into the spotlight with a new crop of developers. Implementing gRPC, JSON-RPC or even the bleeding edge Typescript RPC libraries or solutions has become a popular option for developers. Before diving into the RPC pattern, I would urge developers to understand the problem that RPC solves, and the reason it fell out of favor before implementing it into their own projects.
The main drawback of RPC in my opinion is the tendency towards a tight coupling between the client and the server. The need to provide a productive developer experience by abstracting the communication layer away to make it feel like you are working on a system implemented all on the same system by definition requires the client and server code to be bound to the contracts created by the procedure definitions.
If you’ve ever struggled with working in a legacy codebase tightly coupled to some SOAP based service, constantly struggling with WSDL version and caching issues then you know the long term problems such a tightly coupled solution can cause.
It’s best to approach this trend with a modicum of prudence. If you are a sole developer or a cohesive team of developers, and you are building an application which can benefit from the rapid development that this pattern allows, and you don’t foresee long term issues with a tightly coupled client / server architecture, then this pattern may be right for you!