Added by Peter Lawrey, last edited by Peter Lawrey on Nov 22, 2008  (view change)

Labels:

rmi rmi Delete
Enter labels to add to this page:
Wait Image 
Looking for a label? Just start typing.

Essence RMI framework.

The Essence RMI framework is designed to be a simple low latency, high throughput messaging and RMI framework.

The framework supports messaging latency as low as 9-30 µs over network latency and synchronous RMI calls with a latency of 20-60 µs (over the ping time of the network) depending on the number and type of arguments. In tests, 98% of calls took less than 60 µs longer than the ping time. For example, on a network with a ping latency of 90 µs, the RMI latency was around 110-150 µs.

The framework supports asynchronous or non-blocking RMI calls for high throughput. A single client-server connection can sustain 100K calls/second. However even modest PCs can achieve a throughput of 50K calls/second or more. Essence RMI has an efficient custom POJO serialization which is faster than Java Serialization. (by about 5x) With batching, throughputs of 500K calls/second can be achieved over a network.

Download Essence RMI 1.03 Browse the code Support forum

This release has been tested on Java 5 update 16 and Java 6 update 10.

When could it be used?

Java RMI is standard and flexible. You should use this is if it meets you needs.

Essence RMI has a simpler interface, and is many times faster.

What doesn't it do?

Essence RMI does not support

  • Recursive references efficiently. Such objects need to use Java Serialization.
  • Class Marshalling. Java RMI supports distribution of classes. Essence RMI only distributes method calls and results.
  • UDP packets. Essence RMI is purely TCP to avoid the cost (in terms of latency) of resending lost packets.

Dependencies.

The Essence RMI is about 87KB in size and requires only Apache commons-logging.

Benchmark results.

Essence RMI Performance focusing on latency.
Essence RMI Throughput focusing on call/message throughput.

Sample code.

To proxy a Map.

Server side configuration
// create a map.
ConcurrentMap<Integer, PrimitivePojo> map = new ConcurrentHashMap<Integer, PrimitivePojo>();
// make it available on port 2222
VanillaRmiServer server = Proxies.newServer("test_queue_over_rmi", 2222, map);

// to shutdown the unit test
server.close();
Client side configuration
ConcurrentMap<Integer, PrimitivePojo> client = Proxies.newClient("test_queue_over_rmi-client", hostname + ':' + port, ConcurrentMap.class);
// add multiple entries.
client.putAll(map2);
// Print out the map
System.out.println("Map is "+client);

// close the client and any open connections.
((Closeable)client).close()

The fact the map is remote is largely transparent. If you view the client in the debugger, it will typically look as though the map was local.

To proxy a BlockingQueue.

The unit test RmiQueueTest.test_queue_over_rmi4 has four producers and four consumers sharing a BlockingQueue in a service.

Server side
BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10);
VanillaRmiServer server = Proxies.newServer("test_queue_over_rmi", 0, queue);

To simplify the unit test, port 0 means open on a random unused port.

This producer adds a task number every 5 ms.

Producer code
BlockingQueue<Integer> client = Proxies.newClient("test_queue_over_rmi4-producer", serverURL, BlockingQueue.class);

for (int i = 0; i < 250; i++) {
    assertTrue(client.offer(i));
    Thread.sleep(5);
}

This consumer polls for a task with 200 ms time out. It drains up to 1000 tasks, but stops when a timeout occurs.

Consumer code
BlockingQueue<Integer> client = Proxies.newClient("test_queue_over_rmi4-consumer", serverURL, BlockingQueue.class);

for (int i = 0; i < 1000; i++) {
    final Integer integer = client.poll(200, TimeUnit.MILLISECONDS);
    if (integer == null) {
        System.out.println("Got "+i+" tasks.");
        break;
    }
    passed.incrementAndGet();
}

This prints out the following. You can see a balanced work load with minimal coding effort/complexity.

Got 248 tasks.
Got 252 tasks.
Got 251 tasks.
Got 249 tasks.

The proxied queue appears in the debugger as if it were local (The class of the collection is obviously a Proxy however)

Making asynchronous or non-blocking calls.

The server should implement the blocking call.

interface Server {
    public int add(int a, int b);
}

The client can also implement the non-blocking version. In this case, the method only waits long enough to send the request. The callback is called with the result or the exception thrown.

interface Client extends Server {
    public void add(int a, int b, Callback<Integer> callback);
}

How are exceptions handled?

Q. Say I have a component which throws the following exceptions. What happens on the client side?

interface Server
public void throwsRuntimeException();
public void throwsException() throws BackingStoreException;

A. The client throws the exception the server threw. You can see the server's in the call stack if you printStackTrace() or log the exception/error.

In this example, the server throws the Exception BackingStoreException. Note: you can see the stack trace for the server and the client.

java.util.prefs.BackingStoreException: org.freshvanilla.test exception
 	        at org.freshvanilla.rmi.RmiFailureTest$FaultyServer.throwsException(RmiFailureTest.java:50)
 	        at ~ call to server ~.call(localhost:33765)
 	        at org.freshvanilla.rmi.RmiFailureTest.test_faultyServer(RmiFailureTest.java:82)

What are similar libraries?

All throughputs are with a single client.

Apache MINA - http://mina.apache.org/ - High throughput messaging (but not RMI support). Sustains ~6,000 messages/second with a single client. Implies about 3000 calls/second.
Java RMI - http://java.sun.com/javase/6/docs/technotes/guides/rmi/ - Standard flexible RMI calls with marshalling. Sustains ~2000 calls/second with a single client.
JBoss RMI - http://www.jboss.org/jbossremoting/ - RMI calls sustaining ~3000 calls/second.
Spring RMI - http://www.roseindia.net/spring/springpart4.shtml - RMI calls sustaining ~2500 calls/second.
Hessian - http://hessian.caucho.com/ - Multi platform RMI. >>500 calls/second
Apache XML-RPC - http://ws.apache.org/xmlrpc/ - RPC over XML. >>500 calls/second
Netty - https://www.jboss.org/netty/ - Messaging ~16,000 round-trips/second.
Lipe RMI - http://lipermi.sourceforge.net/faq.php - Performance not stated. Focus is on bandwidth efficiency.
Transparent RMI - http://trmi.sourceforge.net/ - Performance and efficiency not mentioned.
Mermi - https://mermi.dev.java.net/ - RMI for Java Micro Edition.
RMI Proxy - http://www.telekinesis.com.au/ - SSL/XML proxying through firewalls.
RMI Doves - http://ca.geocities.com/rmi_doves/ - RMI HTTP Tunnelling.

Gigaspaces - http://www.gigaspaces.com/ - Distributed data, methods and events.
Coherence - http://www.oracle.com/technology/products/coherence/index.html - Distributed data.
GemStone - [?] - Distributed data and events. 519 µs latency and a throughput of 6,000 call/second. Used top Spec hardware.

Faster solutions.

RTI - RTI - Message and data distribution. 43 µs messaging and up to 3 million messages per second.
Solace - STAC Report: Solace 3230 Content Router - Guaranteed Messaging - 100,000 messages/second guaranteed delivery. 99.9% of messages delivered at or below 1 ms.
Cicso Router - STAC Report: RMDS on Intel Dunnington with Cisco Nexus 10GigE - 11 million updates per second through a single Point-to-Point Server machine with standard frames over RDMS. 4 million using 1 GigE.

Benchmark References.

JBoss RMI, Java RMI & Spring RMI - http://www.jboss.org/jbossremoting/docs/benchmark/performance_benchmark.html

http://daniel.gredler.net/2008/01/07/java-remoting-protocol-benchmarks/
http://www.theserverside.com/news/thread.tss?thread_id=48028
http://www.research.ibm.com/journal/sj/391/baylor.html
http://www.gridtoday.com/grid/2035247.html

Historical Reports

http://www2.lifl.fr/~merle/benchmarking.pdf
http://mjcs.fsktm.um.edu.my/document.aspx?FileName=339.pdf
http://www-csag.ucsd.edu/individual/achien/cs491-f97/projects/hprmi.html