Spreading tasks among many workers can often accelerate their completion. For example, a single person, no matter how strong or fast, could never have built even the smallest of the Egyptian pyramids. However, by distributing the task among thousands of workers, the Egyptians were able to build these great wonders within a single lifetime.
Likewise, some computational problems, beyond the reach of all but the most powerful and expensive supercomputers, can be solved by a collection of modestly powerful and inexpensive "slave" computers. These slave computers are directed by a "master" coordinating computer. The idea of the master-slave relationship is central to a particular style of distributed computing.
Distributed computing has become very popular in recent years for a few other reasons as well. Many complex problems have large data sets associated with them. These data sets are not easy to move from computer to computer. Nevertheless, it might be advantageous to have many computers working on the problem. A powerful server could control that data and distribute the parts of data its clients need. Also, a distributed system is much more fault tolerant than a single computer that brings the operation to a halt if it fails [6].
There are often semantic debates about the new terms client and server. Generally, a server provides a service that is used by a client. One reading would be that the programs doing the work serve their coordinating program. In the example to follow, we consider coordinating the efforts the service, performed by a server analogous to the master pyramid architect. The clients are the slaves that do the work. Try to understand this subtle and potentially confusing point as more of the example is explained.
Java supports distributed computing in a variety of ways; one of the simplest is known as Remote Method Invocation (RMI) [1]. Using the RMI capability of Java, clients may execute remote method calls on a server using a transparent interface. RMI provides a way for Java programs to interact with classes and objects working on different virtual machines on other networked computers. From the perspective of the client, objects on the server may be referenced as if they were local.

In the above diagram, the necessary components of a program using the RMI system are represented as a magical connection between a client and its server. A client is supplied with the interface of methods available from the remote server, but all implementation is left to the server side. In the truest sense, the details of implementing the remote methods are abstracted from the client software.
What really connects the client and server of an RMI system is a layered connection, transparent to developers of most simple applications, operated by the RMI subsystem of the two virtual machines involved.

The stubs and skeletons provide additional abstraction for the Java developer, once an RMI system is established. The code may be written as if all methods and objects are local to the client. The remote reference layer handles reference variables to remote objects, using the Transport layer's TCP/IP connection to the server.
What happens when a client tries to execute a method
provided by the server? As mentioned above, the client has a stub of the
method provided by the server, which executes the actual
implementing class of that service. Any class whose objects are to be
passed over RMI connections must be marked as such by declaring that they
implement the java.io.Serializable interface. All arguments
to a remote method must be either Java primitives or implement that
interface.
A client requests a reference to an object from the server using
the stub on the client side. The server gets the request from
a skeleton on the server side. Between the two is the remote
reference layer, which negotiates the requests by converting
objects into portable form across the network. This
conversion is called marshalling. If the data to
be transferred is neither primitive nor serializable, the
stub will throw a MarshalException
[6]. The idea of serialization is mentioned
again in the example to follow.
KeyBlock CheckerComplete and operable code for this demonstration is available from the author's website, along with instructions [5].
To distribute the work of a large problem, the server coordinates the work of the clients. Clients periodically retrieve their assignments from the server using remote method calls. For this example, I'll examine a distributed computing solution to a problem of cryptoanalysis. A message has been encrypted using a long integer key. For the sake of this example I assume can only be discovered by brute force checking all possible keys.
To implement the most efficient checking of all possible keys, a server
will assign a range of unchecked keys to a client, which will check its
assigned keys in parallel with all other clients. I want clients to
request an assignment from the server in the form of a block of keys. I
need a KeyBlock class, which must be marked as
serializable.
public class KeyBlock
implements java.io.Serializable {
public int start, end;
public KeyBlock (int s, int e)
{
start = s;
end = e;
}
}
The API for Remote Method Invocation is part of the standard
Java Software Development Kit. With RMI, as will all other parts of
the Java API (except java.lang) a class
must be imported using an import declaration before
it can be used in a program. If you need a class and don't know what
package of the Java API it's in, try looking in the online documentation
[2].
The server will assign blocks of keys to clients by returning an object
from the KeyBlockManager class upon their request.
A client side interface
is used to inform clients what services are
available and what to expect:
public interface KeyBlockManager
extends java.rmi.Remote {
public KeyBlock getNext ()
throws java.rmi.RemoteException;
}
On the server side, we implement a class that is executed
when the client makes a remote call. This class creates an instance
of the KeyBlock class, and
returns it.
public class KeyBlockManagerImpl
extends java.rmi.server.UnicastRemoteObject
implements KeyBlockManager {
public KeyBlockManagerImpl ()
throws java.rmi.RemoteException {
super ();
}
public KeyBlock getNext ()
throws java.rmi.RemoteException {
KeyBlock kb = new KeyBlock (nextStartIndex, BLOCK_SIZE);
return kb;
}
}
The developers of the Java remote method system provided a concept called the RMI registry, which runs on a generic port, and informs clients which port has the server to respond to their specific requests [3]. This provides an additional level of abstraction for developers of servers and clients. A registry provides a reference to a client looking for its server. The client code need not include the port where the server is running if it can look up that port dynamically from a registry. What port the registry uses is usually not such a difficult question to answer. Port 1099 is considered a default port for an RMI registry.
Using the RMI registry, a client can obtain a reference to an object
that resides on a different computer (or maybe just a different virtual
machine on the same computer) and call its methods as if that object were
in the local virtual machine. Pay careful attention to the lines of code
where the server code calls
(Naming.rebind)
to get a port assignment from the RMI registry. Notice also later
that the client looks up the server
(Naming.lookup)
in the registry and then makes
a request for an object reference. This process of using the
rmiregistry
is very similar to what the
portmap
daemon does on a Unix system to facilitate Remote
Procedure Calls.
The server must coordinate requests for key block assignments from the
clients. Assume that key blocks are a standard size and are assigned
sequentially.
public class KeyBlockManagerServer {
private static final String RMI_URL =
"rmi://RMIRegistryServer:1099/KeyBlockManagerService";
public KeyBlockManagerServer () {
try {
KeyBlockManager kbm = new KeyBlockManagerImpl ();
Naming.rebind (RMI_URL, kbm);
}
catch (Exception e) {
e.printStackTrace (System.out);
}
}
public static void main (String [] args)
{
new KeyBlockManagerServer ();
}
}
The client finds the server from the registry, and then gets a reference
to a remote object that implements the
KeyBlockManager interface from the
RMI system. The client then enters a
loop by executing a remote call to the remote object. The call to the remote
object is handled identically to a call to a local object.
The method call is transferred to the server, which executes
the implementation and returns the assignment as a serialized object of
the KeyBlock class.
public class KeyBlockClient {
private static final String RMI_URL =
"rmi://RMIRegistryServer/KeyBlockManagerService";
public static void main (String [] args) {
try {
KeyBlockManager kbm =
(KeyBlockManager) Naming.lookup (RMI_URL);
while (true) {
KeyBlock kb = kbm.getNext ();
checkBlock (kb);
}
}
catch (Exception e) {
e.printStackTrace (System.err);
}
}
}
To actually make this RMI system operational, the source code files
must be compiled into Java bytecode class files and distributed to the
appropriate places. As a Java programmer you are already familiar with the
javac tool to produce compiled bytecode class files.
Part of the standard Java Software Development Kit (SDK) is
another tool, rmic, which produces the stub and skeleton
files necessary for the Remote Reference Layer mentioned earlier from the
source code of the implementation [4].
The command works like this:
% javac KeyBlockManagerImpl.java
% rmic KeyBlockManagerImpl
Which produced the following new .class files:
KeyBlockManagerImpl
|
Compiled Java bytecode of the implementation, to be executed for a remote call. |
KeyBlockManagerImpl_Skel
|
The skeleton of the object providing remote methods, used by the server side of the reference layer. |
KeyBlockManagerImpl_Stub
|
The stub of the object providing remote methods, used with the interface by the client side of the reference layer. |
The respective class loaders must also have class files for the following available:
|
|
|
| Remote service interfaces | Remote service interface definitions |
| Remote service implementations | Stubs for the remote implementations |
| Skeletons for the implemented classes | Any values returned by remote calls |
| Stubs for the implemented classes | Any other classes the client needs |
| Any other classes the server needs |
Note especially that clients must have available the class definition
of any return type of a remote method. In our primitive example, the client's
class loader must find KeyBlock.
To start an RMI registry and bind the server to it:
This prepares two Java virtual machines to handle requests for
remote objects from clients.
% rmiregistry &
% java KeyBlockManagerServer &
The client makes a request to the registry to find the
appropriate server. Then it makes a request to its server
for a reference to an object that represents its task.
% java KeyBlockClient
There is another new technology emerging that may change the face of cross platform distributed and client-server computing. Developers are now working closely to make the processing much less Java-specific. One idea that has much potential is Common Object Request Broker Architecture (CORBA). As the name implies, CORBA is a standard for location transparency and language independence between creators and users of objects. A client would neither know nor care about where an object was or in what language it was implemented [7].
The way RMI is implemented now, with serialized object streams, is inherently too slow. You will have noticed a speed problem even in the trivial example above. A service distributed with CORBA will be faster and more universal than a similar service distributed with RMI. The main reason for the performance increase will be the Internet InterOperability Protocol (IIOP), which quickly creates very high speed connections between objects while minimizing overhead [7].
Hopefully you now have a glimpse of the power of distributed computing with remote calls, and will consider implementing solutions in that way. If you wish to download the full source code of a functional version of this distributed application, visit the author's web site [5]. Feel free to experiment with it. Enjoy!
rmiregistry Tool Documentation --
http://java.sun.com/j2se/1.3/docs/tooldocs/solaris/rmiregistry.htmlrmic Tool Documentation --
http://java.sun.com/j2se/1.3/docs/tooldocs/solaris/rmic.html
Last Modified:
Location: www.acm.org/crossroads/xrds6-5/ovp65.html