Seminar 5: RMI, Code migration and Offloading using Java
Goal: To understand code migration through transparent communications in distributed systems.
Description: Code migration from one process to another occurs using RPC (Remote Procedure Call) implementations. However, code also can be captured during runtime and migrated to external processes without modifying objects (binaries) of the application being executed. This is called code offloading. In this practical seminar, we will explore how to migrate code during runtime between processes. We will combine RPC and socket primitives to achieve this.
Definitions: Java reflection is an utility that can be used to capture runtime details of objects (instantiated classes) while they are in execution. Java reflection uses RMI (Remote Method Invocation) to call blocks of code. In parallel to this, Sockets are used in client-server communications to enable a two-way communication link to exchange messages.
Exercise: Get acquaintance with the Java reflection utility that comes with any JVM. Implement code migration at method level of any sorting algorithm, e.g., bubble sort, quick sort, etc. The migration needs to happen via sockets between a client and a server. In this process, the client captures the details of code execution during runtime and sends it to the server (in a serialized manner) for processing; the server then takes those details and executes the code. The result is send back to the client, such that it can continue with the execution of the algorithm transparently.
- The migration process can be implemented in a single machine, but specify two separate instances, one for a client, and one for the server.
- The code for the above implementation is given here for you. (Code)
- Check the code, try to understand the Code Pipeline and replicate it. Use the last 4 numeric digits of your enrolment ID as the Port Number. (For example, if your enrolment ID is C12345, use 2345 as the port number). This is to ensure everyone tries to replicate the code.
WORKING PIPELINE OF THE ABOVE IMPLEMENTATION:
- As you can see in the code repository, there are four files - Client.java, Server.java, Message.java and Sorter.java. The main classes are the Client and Server classes. The Client is actually just the client stub and the server is the server stub.
- The Client class is used to send a class instance (sorter), a method of that class (sort), and its corresponding parameters (4,10), to the Server over a socket.
In Client.java
//in main function
int[] results = (int[])client.remoteCall(sorter, "sort", 4, 10);
'
// in remoteCall Method
Socket socket = new Socket("Server IP ", ServerPortNumber);// Establish a socket communication with the server. If you use the same computer to run both client and server, use 127.0.0.1 as the IP Address
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
System.out.println("Client stub: Connected to server stub!");
Message msg = new Message(method, paramValues, instance);
System.out.format("Client stub: Calling method 's' ...\n", method,
instance.getClass().getName());
out.writeObject(msg); // write the message object into socket stream to server.
- The Server then unpacks the Message and executes the method. The result is captured and sent back to the Client over the same socket. The Client then closes the connection and the Server ends the session, and starts listening on the same port for a new connection from a Client.
In Server.java
while ((msg = (Message)in.readObject()) != null) {
System.out.format("Server stub: Received method 's' ...\n", msg.methodName,
msg.classInstance.getClass().getName());
Object result = processMessage(msg);
out.writeObject(result);
System.out.println("Server stub: Message processed, returning results...");
- This implementation makes use of the Sorter class(Sorter.java, which is a very basic serializable class containing a single method, `sort(Integer, Integer)`, which is invoked by Server.
- How to Compile and Run the above Implementation: To compile the files: (Keep in mind that these commands should be issued within the terminal from the same working directory where your java files are located.)
$ javac Server.java $ javac Client.java
In order to run the compiled class files first run java Server <portName> in one terminal (or command window) and run java Client <portName> in a separate terminal. Note that the port names must be the same. For illustration,
$ java Server 3333
and Then in a separate terminal:
$ java Client 3333 $ java Client 3333 $ java Client 3333
The snapshots are shared for your reference:
Server Side: Client Side:
Main cruxes behind this or What we Want to achieve:
- The client stub sends a marshalled object, method and relevant variables to the server stub.
- The server stub receives the message.
- The server stub processes the message and executes the method (Bubble sort or Quick Sort)
- The server stub writes the result(s) into the socket buffer for the client.
- The client stub receives the result and outputs it to the terminal screen.
- The client stub closed the connection and sent an EOF to the server socket.
- The server closed the connection after reading an EOF and started listening again for a new connection on the same port.
Deliverables: Zip file containing the source files (src) and compiled class files and short demo video showing the complete working of the above implementation.