A topic in the Open Knowledge Graph — a free, open map of 15,495 topics and the order to learn them in.
A remote procedure call lets a program call a function that runs on another machine using syntax that looks like an ordinary local call. Generated code called stubs does the hidden work: the client stub marshals (serializes) the arguments into a network message, and the server stub unmarshals them, calls the real function, and sends the result back. The classic design was described by Andrew Birrell and Bruce Nelson in 1984, and modern frameworks such as gRPC follow the same pattern. The local-call appearance is only partial, because remote calls add network delay, can fail partway, and cannot share memory.
Write an interface definition, for example a small Protocol Buffers service with one method, generate the stubs, and call the method from a client. Then make the server slow, kill it mid-call, and drop replies, and ask students what the client can conclude in each case. Use these experiments to introduce at-most-once and at-least-once semantics and the value of idempotent operations.
Writing networked programs with raw sockets means building byte messages by hand, sending them, waiting and parsing replies. It is tedious and easy to get wrong. A remote procedure call hides that work behind a familiar abstraction: a function call. The client writes `balance = bank.get_balance(account_id)`, and the system arranges for `get_balance` to run on a server and return its answer.
The machinery has a standard shape, described by Andrew Birrell and Bruce Nelson of Xerox PARC in 1984. The programmer first writes the remote interface in an interface definition language (IDL). In gRPC that language is Protocol Buffers. A tool generates two pieces of code. The client stub has the same signature as the real function. When called, it marshals the arguments into a byte format, sends a request message and waits. The server stub receives the message, unmarshals the arguments, calls the real implementation and marshals the return value into a reply. Marshalling also settles representation differences between machines, such as byte order.
The local-call disguise is useful but imperfect. Three differences matter. First, latency: a local call takes nanoseconds, while a remote call takes at least a network round trip, often a millisecond or more. Second, no shared memory: arguments are copied, so passing a pointer or a large shared object does not work as it would locally. Third, and most important, partial failure. A local call either returns or the whole program fails. A remote call can fail in pieces. The request can be lost, the server can crash before or after doing the work, or the reply can be lost.
When a reply does not arrive, the client cannot tell which of these happened. Systems respond with defined call semantics. With at-least-once semantics, the client keeps retrying until it gets a reply, so the procedure may run more than once. That is safe only for idempotent operations, which have the same effect when repeated. With at-most-once semantics, the client gives each request a unique ID and the server discards duplicates, so the procedure never runs twice, though it may not run at all. True exactly-once execution needs extra machinery, such as durable logging of request IDs and results.
RPC frameworks such as gRPC and Apache Thrift remain central to building services. Good designs treat every remote call as something that can be slow or fail, and they set timeouts, retries and idempotence on purpose.