CS102: Remote Procedure Call and Java RMI

Copyright © 1999, Kenneth J. Goldman

Introduction


Our discussion of interprocess communication so far has focused on communication through streams provided by TCP sockets. However, it is possible to build other kinds of IPC abstractions on top of the basic infrastructure provided by the network. One common alternative, called remote procedure call (RPC), provides the programmer with the illusion that a method call is being made on a local object, when in fact the object actually resides in some other process.

Normally, we think of calling a method like this:


  1. Object x (the caller) puts the parameters on the stack
  2. Method body in object y (the callee) is invoked, parameter values are seen on the stack
  3. return value is computed, placed on stack
  4. control is passed back to the caller, where the return value is read off the stack

However, when the caller and callee are in different processes, they do not have access to a shared stack, so for RPC, we need to handle the communication of parameter values and return values differently.

How RPC Works


  1. We determine where the callee is located (see below)
  2. We package up the parameter values (called "marshaling") and send them over the network. Then we wait.
  3. The network delivers the parameters (and other relevant info about the call, (like caller and callee)
  4. The parameters are unpacked (called "demarshaling") and passed to the appropriate method for execution
  5. The return value is packaged up and sent over the network to the caller
  6. The return value is delivered
  7. The waiting caller receives the return value and continues

(Note: In the case of non-blocking RPC, the caller does not wait for the return value. This is useful if there are no return values, or if the caller doesn't use the return value (right away)).

OK, but how do we find the callee so we can send the parameters?

Making a call on a remote object requires knowing on what machine the object is located, so a connection can be opened to send the parameters. Then, even within the machine, when the parameters arrive, the RPC system needs to know where to actually invoke the method. This functionality is generally provided by a special well-advertised server called a "name server", which keeps a mapping from names of registered objects to their locations . (Sort of like a phone book.)

So, the callee must first register its name and location in the name server. The caller needs to know only the name, and the location can be looked up when the call is made. In some systems, the name server forwards the parameters to the callee. In others, it returns the location directly to the caller. Often, on subsequent calls, the caller will bypass the name server.



Remote Method Invocation


A simple RPC mechanism provided by Java is called Remote Method Invocation (RMI).

Each object to be used remotely must implement a remote interface. For example,

import java.rmi.*;
interface SquareServerRemote extends Remote {
int getSquare(int i) throws RemoteException
}


The callee must implement the interface and register itself n the name server when it starts up.

import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.net.*;

public class SquareServer extends UnicastRemoteObject implements SquareServerRemote {

int getSquare(int i) throws RemoteException {
return i*i;
}

public static void main(String args[]) {
System.setSecurityManager(new RMISecurityManager());
try {
SquareServer me = new SquareServer();
Naming.bind("//hilton:2000/SquareServer",me);
} catch(Exception e) {
e.printStackTrace();
}
}
}


RMI Registry


Before starting the server program, the RMI registry needs to be running on the machine: The name of the program to start is rmiregistry , so
on windows:
start rmiregistry 2000 <--- desired port number
on unix:
rmiregistry 2000 &

Also, you need to run both the caller and callee programs through not only the standard java compiler, but also through the rmi compiler (called rmic) in order to generate the code for marshaling and demarshaling the parameters (all arguments must be Serializable).

The caller code would look like this:
import java.rmi.*;
import java.rmi.registry.*;

public class SquareClient {
public static void main(String[] args) {
System.setSecurityManager(new RMISecurityManager());
try {
/* Note: With RMI, you need to know the location of the registry to find and object. Then all communication is directly with the remote object. */
SquareServerRemote ssr = (SquareServerRemote) Naming.lookup("//hilton:2000/SquareServer");
int result = ssr.getSquare(6);
System.out.println("The square of 6 is " + result);
} catch(Exception e) {
e.printStackTrace();
}
}
}


Note: With RMI, you need to know the location of the registry to find an object. Then all communication is directly with the remote object.