RMI has lot of overhead, but even worse part is how it operates as ”black box”. Thus various mechanisms are hidden deeply inside framework, there ain’t any finetuned control over how, for example, sockets are pooled and such.

So is there alternatives?

For simplicity lets ignore all alternatives, which are not supporting standard Java Serialization.
Why so, you may ask?

Justification is very simple, all approaches steering away from standard serialization, are re-inventing wheel, and if existing codebase is using Serializable, then converting it into another custom serialization doesn’t make any sense, since very likely custom serialization cannot beat java serialization anyway. Reason why it’s hard to beat java serialization is that it’s able to rely into internals of the JVM when doing its’ logic, and those internal native APIs cannot be used outside of it. Secondly many alternatives claim (& even proof) that they are faster than standard serialization. But, there comes the problem, they haven’t implemented the most important parts of the logic, like serialization of object graphs. For example, API makes assumption that none of the objects are referencing each other, thus either referenced objects are serialized multiple times (== memory leak in de-serialize), or serialization fails when encountering cyclic reference (== infinite loop).

So lets stay away from new bugs introduced by alternative serialization mechanisms, and lets concentrate into alternatives how remote calls (with serialized objects) can be done between JVMs.

Second simplification is that we assume that we have full control of both server and client side, and we don’t need to care about compatibility with other applications.

Third requirement is that call mechanism must support streaming, i.e. it must be possible to serialize objects into stream. i.e. alternative, which would first serialize and create message in JVM memory before sending it, is out of question. Reason for this is that doing so would require exhaustive amount of memory when large objects are serialized.

Fourth issue is that usage of compression to reduce transmitted data, is not either issue here. We can assume that compression of socket stream (also encryption) is something which happens in lower level of communication, and doing such in this ”RMI” level is then pointless (and likely less efficient).

This leads into some possiblities, based into quick search:

LipeRMI

… light weight framework which provide full remote method invocation calls between VMs. …

Advanced Socket Programming

… Combining low-level sockets and object serialization gives you a powerful, efficient alternative to RMI that enables you to transport objects over sockets and overcome the overhead incurred in using RMI. …

Question which remains is: Does it make any sense? Is it really sensible to reivent wheel and do own ”RMI”? And answer to this depends.

It might make sense if…

  • it’s desired to have strict control of sockets
  • all the extra logics supported by RMI are not needed (like remote callback objects, remote class loading and such are not needed)
  • it’s necessary to squeeze all speed possible and reduce overhead

Update 21.11.2009
Some more leads for investigation:
java serialization alternative
Efficient RMI and Object Serialization
Efficient Java RMI for parallel programming
KaRMI

Update 22.11.2009
Wireless Java RMI
Efficient Support of Java RMI over Heterogeneous Wireless Networks
Wireless Optimization for Java RMI
Java: Expected overhead of the rmi protocol
Java Remoting: Protocol Benchmarks

Basic message appears to be: RMI has issues with low-bandwidth, long latency networks (especially considering wireless networks case).

It’s, however, important to notice that few of these ”RMI is slow” problems are actually original Java v1.1 implementation issues, while current newer version has already fixed some of these original design problems. So, all the references must be digested with care.

And then some short code sample:

KI-RMI: Simple remote call over sockets
[code lang=”java”]
/**
* Pool Socket connections
*/
ConnectionPool {
Conn get()
release(Conn)
}

/**
* Single pooled socket connection to server
*/
Conn {
Socket socket;

/**
* Connect to server lazily, on demand
*/
connect() {
if (socket == null) {
// connect to server
}
return socket;
}

/**
* send data into socket and read result back. System can reuse
* byte buffers to avoid reallocation
*/
byte[] invoke(byte[] data) {
Socket socket = connect();
socket.write(data.length);
socket.write(data);
byte[] result = socket.read();
return result;
}
}

/**
* Proxy for making calls in Remote interfaces
*/
ServiceProxy implements InvocationHandler {
Object invoke(Object proxy, Method m, Object[] args) {
ConnectionPool pool = getProvider().getConnectionPool();
Conn conn = pool.get();
byte[] data = compress(m, args);
byte[] result = conn.invoke(data);
pool.release(conn);
return decompress(result);
}
/**
* compress call into message. System can reuse
* byte buffers to avoid reallocation
*/
byte[] compress(Method m, Object[] args) {
Call call = new Call(m, args);
// serialize & compress
return data;
}

/**
* Resolve data back to objects
*/
Object decompress(byte[] data)
}

/**
* Actual call, which is responsible for encoding/decoding
* call from input/output stream. Call itself isn’t actually
* serializable, but just encoder/decoder of data
*/
Call {
/**
* Each Remote API can be unique identifier, which
* is well known by both client & server
*/
short remoteId;
/**
* Algorithm: Stable sort of methods in Remote
* (takin in account name, return value, args),
* index of method based in sorted result
*/
short methodId;

Call(Method m, Object[] args)

writeExternal(out) {
out.writeInt(remoteId);
out.writeInt(methodId);
for (arg : args) {
out.write(arg);
}
}

readExternal();
}

/**
* Provider for connections, this class knows the server.
* Connection to server is established only on ”on-demand”
* basis
*/
RMIProvider {
RMIProvider(host, port)

/**
* Get proxy for remote API. Remote server is NOT
* contacted at all at this point of time
*/
Remote get(Class cls) {
return Proxy.newProxyInstance(
cls.getClassLoader(),
cls,
new ServiceProxy(cls));
}

ConnectionPool getConnectionPool()
}
[/code]

Update 24.11.2009
Essence RMI framework

This one gives some ideas of how to implement own framework.

Performance Test Reports

Update: 3.1.2010
Java RMI

Update:12.2.2010
Java RMI: Serialization (at O’reilly)
More compact (==faster) serialize

/ java

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *

This site uses Akismet to reduce spam. Learn how your comment data is processed.