Remote Procedure Call (RPC)

Mohit Sharma
6 min readJun 4, 2024

--

Remote Procedure Call (RPC) is an important abstraction for processes to call functions in other processes, even if those processes are on different computers. It was proposed by Birrell and Nelson in 1984, and is now implemented and used in most distributed systems, including cloud computing systems.

RPC works by hiding the details of network communication from the programmer. Instead of having to write code to explicitly send and receive messages over the network, the programmer can simply call a remote function as if it were a local function. The RPC framework takes care of all the details of marshalling and unmarshalling the function arguments and results, and sending them over the network.

RPC has a number of advantages, including:

  • It makes it easier to develop distributed applications.
  • It allows code reuse.
  • It can improve performance by distributing the workload across multiple computers.
  • It is scalable and fault-tolerant.

RPC works by marshalling the function arguments and results into a message, and then sending that message to the remote process. The remote process then unmarshals the message and calls the requested function. The results of the function call are then marshalled and sent back to the calling process.

RPC is a powerful tool for developing distributed applications. It allows developers to write code as if all of the functions were in the same process, even if they are actually spread across multiple machines. This makes it easier to develop and maintain distributed applications.

RPC is also a very efficient way to communicate between distributed processes. The marshaling and unmarshaling overhead is relatively small, and the RPC framework takes care of all the details of sending and receiving messages over the network.

RPC is widely used in cloud computing systems. For example, Amazon Web Services (AWS) offers a number of RPC services, such as Simple Queue Service (SQS), Simple Notification Service (SNS), and Simple Email Service (SES). These services allow developers to build distributed applications without having to worry about the details of network communication.

The counterpart of RPC in object-based settings is called Remote Method Invocation (RMI). RMI is similar to RPC, but it is specific to object-oriented programming languages. RMI allows developers to call methods on remote objects as if they were local objects.

When should RPC be used?

RPC should be used when communicating between processes on different machines, or when it is necessary to access objects that are located in other processes. It should not be used for communication between internal subsystems, as LPC is better suited for this purpose.

RPC Call Semantics

RPC call semantics are the guarantees that an RPC system can provide about the execution of a remote function call.

There are three main RPC call semantics:

  • At most once: The function may be executed at most once. This means that if the client retransmits a request due to a failure, the server will ignore the duplicate request.
  • At least once: The function will be executed at least once. This means that if the client does not receive a response to a request, it will retransmit the request until it receives a response.
  • Maybe: The function may or may not be executed. This is the weakest RPC call semantics, and it is typically only used for best-effort communication.

Each RPC call semantics has its own advantages and disadvantages. At most once semantics are the simplest to implement, but they can lead to data loss if a request is dropped or if the server fails before executing the function. At least once semantics are more complex to implement, but they guarantee that the function will be executed at least once, even if there are failures. Maybe semantics are the easiest to implement, but they provide the fewest guarantees.

The most common RPC call semantics is at least once semantics. This is because at least once semantics guarantees that the function will be executed at least once, even if there are failures. This is important for many applications, such as database transactions and financial transactions.

However, at least once semantics can lead to duplicate function executions if the client retransmits a request due to a failure and the server has already executed the function. This can be a problem for applications that are sensitive to duplicate executions, such as banking applications and accounting applications.

To avoid duplicate function executions, RPC systems that support at least once semantics typically use a mechanism called idempotency. Idempotency is the property that a function can be executed multiple times without producing any different results. RPC systems use idempotency to ensure that duplicate function executions do not have any side effects.

RPC Component

Client

  • Client stub: This is a proxy for the remote function on the server. It has the same function signature as the callee() function on the server. This allows the same caller() code to be used for both LPC and RPC.
  • Communication module: This module forwards requests and replies to the appropriate hosts.

Server

  • Dispatcher: This module selects which server stub to forward the request to.
  • Server stub: This module calls the callee() function on the server. It also allows the callee() function to return a value.

The RPC architecture works as follows:

  1. The client calls the client stub.
  2. The client stub marshals the function arguments into a message and sends it to the communication module.
  3. The communication module forwards the message to the appropriate host.
  4. The dispatcher on the server receives the message and selects the appropriate server stub to forward the message to.
  5. The server stub unmarshals the function arguments from the message and calls the callee() function on the server.
  6. The callee() function executes and returns a value.
  7. The server stub marshals the return value into a message and sends it back to the client stub.
  8. The client stub unmarshals the return value from the message and returns it to the caller.

Generating Code

RPC middleware systems generate the code for the remaining components (client stub, server stub, and communication module) automatically from the function signatures (or object interfaces in object-based languages). This makes it easier for programmers to develop distributed applications, as they do not need to worry about the details of network communication and marshaling/unmarshaling.

One example of an RPC middleware system is Sun RPC. Sun RPC uses a compiler called rpcgen to generate the code for the client stub, server stub, and communication module from a Sun XDR interface representation. The Sun XDR interface representation is a language-neutral way to describe the data types that are exchanged between the client and server.

Other examples of RPC middleware systems include CORBA, Java RMI, and gRPC. All of these systems generate the code for the remaining components automatically from the function signatures (or object interfaces in object-based languages).
RPC middleware systems are a valuable tool for developing distributed applications. They make it easier for programmers to write code that is scalable, reliable, and efficient.

Marshalling / Unmarshalling

Different architectures use different ways of representing data. This is known as endianness.

  1. Big endian architectures store the most significant byte of a multi-byte data type at the lowest address, and the least significant byte at the highest address. IBM z and System 360 architectures are big endian.
  2. Little endian architectures store the least significant byte of a multi-byte data type at the lowest address, and the most significant byte at the highest address. Intel architectures are little endian.

RPC middleware systems use a common data representation (CDR) to represent data that is exchanged between the client and server. This CDR is platform-independent, so that the client and server can be running on different architectures.
When the client makes an RPC call, it marshals the function arguments into CDR format. This involves converting the data types to the CDR data types and packing the data into a message. The middleware system then sends the message to the server.

When the server receives the message, it unmarshals the function arguments from CDR format. This involves converting the CDR data types to the server’s platform-dependent data types and unpacking the data from the message. The server then calls the callee function with the unmarshalled function arguments. The return value of the callee function is marshalled into CDR format and sent back to the client. The client then unmarshals the return value from CDR format and returns it to the caller.

The use of a CDR makes it possible for RPC middleware systems to support communication between clients and servers running on different architectures.

Marshalling and unmarshalling can be complex, but RPC middleware systems take care of all the details for the programmer. This makes it easy to develop distributed applications that can communicate between clients and servers running on different architectures.

Hope you enjoyed reading. I’m always open to suggestions and new ideas. Please write to me :)

--

--