I’ve come across a lot of business logics in my company that take advantage of the RMI remoting technology. It’s said that the performance of RMI is not only poor, but also is too heavy. However some serious articles and benchmark tests argue that it’s not bad. And I also did some simple performance tests among some remoting technologies, and the result reveals RMI is the best at least in the LAN.
The following is a summary about the most important skills that can promote the speed of RMI as well as a personal RMI debugging logic.
Default Serialization is slow and we should use the Externalizable interface to transfer only those necessary properties and fields over the network to reduce the network traffic and response time.
Here’s an example:
public class SerializedClass implements Serializable { private String aString; private int anIntA; private int anIntB; private float[] floatArray; private Dimension dimensionA; private Dimension dimensionB; // No more code related to serialization! : : public class ExternalizedClass implements Externalizable { private String aString; private int anIntA; private int anIntB; private float[] floatArray; private Dimension dimensionA; private Dimension dimensionB;
public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(aString); out.writeInt(anIntA); out.writeInt(anIntB); out.writeObject(floatArray); out.writeObject(dimensionA); out.writeObject(dimensionB); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { aString = (String)in.readObject(); anIntA = in.readInt(); anIntB = in.readInt(); floatArray = (float[])in.readObject(); dimensionA = (Dimension)in.readObject(); dimensionB = (Dimension)in.readObject(); } // Etc.
|
The above code snippet is quoted from Accelerate your RMI programming. If you’re interesting in the benefit that gained from this method, I suggest you read the article.
Java would not transfer the null value over the network and this could also make your object compact.
If you are really sure about a property that is not valuable for you remote user, then make it a local property with the transient key word. Use the transient keyword to define fields to avoid having those fields serialized. Examine serialized objects to determine which fields do not need to be serialized for the application to work.
We need to bind our remote service to a specific IP address from time to time if there are multiple network interfaces on a server. And as now we are more often using Spring to expose our remote service, I will show the Spring way to specify the IP address to bind to at run time.
<bean id="xxxRMIServiceExporter" class="org.springframework.remoting.rmi.RmiServiceExporter"> <property name="serviceName"> <value>BillingRemoteService</value> </property> <property name="service"> <ref bean="billingRMIService"/> </property> <property name="serviceInterface"> <value>com.xxx.xxx.BillingRemoteService</value> </property> <property name="registryPort"> <value>1199</value> </property> <property name="registryHost"> <value>192.168.21.161</value> </property> </bean>
We’ve been stuck in the RMI in our products’ previous development version and have limited knowledge about RMI, and hereafter we could only fix some problems by our experience. We want to know how many remote services have been registered, want to know if a particular service is up and running and list the methods on a service, etc.
Here’s the interface:
package com.bee.fw.rmi; import java.lang.reflect.InvocationTargetException; import java.rmi.Remote; /** * @author Ginge */ public interface RmiServiceManager { /** * List all the registered Remote Services bound to the port at the host * @param host * @param port * @return */ String [] listRemoteObjects(String host, int port); /** * Find the registered Remote Service bound to the port at the host * @param host * @param port * @param remoteObject * @return * @throws RemoteObjectNotFoundException */ Remote findRemoteObjects(String host, int port, String remoteObject) throws RemoteObjectNotFoundException; /** * Invoke the remote method on the remote Object * @param host * @param port * @param remoteObject * @param methodName * @param args * @return * @throws RemoteObjectNotFoundException * @throws IllegalArgumentException * @throws IllegalAccessException * @throws InvocationTargetException * @throws NoSuchMethodException */ Object invokeRemoteMethod(String host, int port, String remoteObject, String methodName, Object ...args) throws RemoteObjectNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException ; }
And the implementation, it requires at least Spring 2.0.5:
package com.bee.fw.rmi; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.rmi.AccessException; import java.rmi.NotBoundException; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.ArrayList; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.SimpleTypeConverter; /** * * @author Ginge * */ public class RmiServiceManagerImpl implements RmiServiceManager { private Log log = LogFactory.getLog(this.getClass()); private SimpleTypeConverter simpleTypeConverter = new SimpleTypeConverter(); private Registry getRegistry(String host, int port) { Registry registry = null; try { if(log.isDebugEnabled()) { log.debug("Locating registry at host["+host+"] on port["+port+"]"); } registry = LocateRegistry.getRegistry(host, port); } catch (RemoteException e) { if (log.isErrorEnabled()) { log.error("Failed to locate Registry at host [" + host + "] on port [" + port + "]", e); } } if(log.isDebugEnabled()) { log.debug("Registry found."); } return registry; } @Override public Remote findRemoteObjects(String host, int port, String remoteObject) throws RemoteObjectNotFoundException{ Registry registry = this.getRegistry(host, port); if (registry != null) { try { return registry.lookup(remoteObject); } catch (AccessException e) { if (log.isErrorEnabled()) { log.error("RMI object ["+remoteObject+"] Access denied.",e ); } } catch (RemoteException e) { if (log.isErrorEnabled()) { log.error("RMI object ["+remoteObject+"] RemoteException.", e); } } catch (NotBoundException e) { if (log.isErrorEnabled()) { log.error("RMI object ["+remoteObject+"] NotBoundException.",e ); } } } throw new RemoteObjectNotFoundException("Remote Object ["+remoteObject+"]"); } private Method findMethodWithName(Remote remote, String methodName, int argsLength) throws NoSuchMethodException { Class objectClass = remote.getClass(); Method [] methods = objectClass.getMethods(); for(Method method : methods) { if(method.getName().equalsIgnoreCase(methodName) && method.getParameterTypes().length == argsLength) { return method; } } throw new NoSuchMethodException("No such Method ["+methodName+"]" ); } private Object invokeMethod(Object obj, Method method, Object ...args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Class [] parameterTypes = method.getParameterTypes(); if((args != null && args.length != parameterTypes.length) || (parameterTypes.length == 0 && args != null)) { throw new IllegalArgumentException("The input args length doesn't match the expected parameter length."); } List<Object> arguments = new ArrayList<Object>(); if(args != null) { for(int i = 0; i < parameterTypes.length; i ++) { Object converted = simpleTypeConverter.convertIfNecessary(args[i], parameterTypes[i]); arguments.add(converted); } } return method.invoke(obj, arguments.toArray(new Object[arguments.size()])); } @Override public String [] listRemoteObjects(String host, int port) { // TODO Auto-generated method stub Registry registry = this.getRegistry(host, port); if (registry != null) { try { return registry.list(); } catch (AccessException e) { if (log.isErrorEnabled()) { log.error("RMI Access denied, failed to list.",e ); } } catch (RemoteException e) { if (log.isErrorEnabled()) { log.error("RMI RemoteException.", e); } } } return null; } @Override public Object invokeRemoteMethod(String host, int port, String remoteObject, String methodName, Object... args) throws RemoteObjectNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Remote remote = this.findRemoteObjects(host, port, remoteObject); Method method = this.findMethodWithName(remote, methodName, (args != null ? args.length : 0)); return this.invokeMethod(remote, method, args); } }
It’s very easy to write a utility tool to debug RMI using the above code by now. And here’s the screen snapshot of my tools.
1) Accelerate your RMI programming
3) Java remoting protocol benchmarks
4) Middleware remoting protocol migration