In this blog post I'm going to return to one of my
previous post about JAX-RS Message Body Providers using
Kryo serialization framework. Today I'll show you performance statistics and compare it with some of Jersey build-in providers.
Comparing to ...
I have chosen to compare my Kryo provider with:
- JAXB RI providers to read from and write to XML format.
- Jackson providers to read from and write to JSON format.
- Custom provider that reads and writes test entity (Person class) by itself. (Note: Used format is really simple and implementation is Person specific, uses artificial MIKME type application/person.)
- Just plain text provider to read and write String only. It uses Jersey build-in provider.
The Kryo provider has been introduced in
previous blog post. I have done one change since then. Performance of Kryo provider was several times improved using
KryoPool:
@Consumes("application/x-kryo")
@Produces("application/x-kryo")
public class KryoMessageBodyProvider implements MessageBodyWriter<Object>,
MessageBodyReader<Object> {
private final KryoPool kryoPool;
public KryoMessageBodyProvider() {
final KryoFactory kryoFactory = new KryoFactory() {
public Kryo create() {
final Kryo kryo = new Kryo();
// configure kryo instance, customize settings
return kryo;
}
};
kryoPool = new KryoPool.Builder(kryoFactory).softReferences().build();
}
//
// MessageBodyWriter
//
@Override
public long getSize(final Object object, final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType) {
return -1;
}
@Override
public boolean isWriteable(final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType) {
return true;
}
@Override
public void writeTo(final Object object, final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType,
final MultivaluedMap<String, Object> httpHeaders,
final OutputStream entityStream)
throws IOException, WebApplicationException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final Output output = new Output(baos);
kryoPool.run(new KryoCallback() {
public Object execute(Kryo kryo) {
kryo.writeObject(output, object);
return null;
}
});
output.close();
entityStream.write(baos.toByteArray());
}
//
// MessageBodyReader
//
@Override
public boolean isReadable(final Class<?> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType) {
return true;
}
@Override
public Object readFrom(final Class<Object> type, final Type genericType,
final Annotation[] annotations, final MediaType mediaType,
final MultivaluedMap<String, String> httpHeaders,
final InputStream entityStream)
throws IOException, WebApplicationException {
try {
final Input input = new Input(entityStream);
final Object object = kryoPool.run(new KryoCallback() {
public Object execute(Kryo kryo) {
return kryo.readObject(input, type);
}
});
input.close();
return object;
} catch (Exception e) {
throw new WebApplicationException(e);
}
}
}
Test environment
Each test application was executed using
Grizzly HTTP Server, no servlet container, on 4 core VM. And load has been produced from another 2 client VMs using
wrk - a HTTP benchmarking tool. GET, POST and PUT HTTP methods was measured. It is necessary to say each test works just with one instance of Person entity. It means data load is small. Test's focus is on throughput measurement.
Test results
Following table shows number of requests per second for each tested provider and mentioned HTTP method:
| GET | POST | PUT |
XML (JAXB RI) | 21705 | 11316 | 12080 |
JSON (Jackson) | 22097 | 18590 | 20116 |
Kryo | 21533 | 18371 | 21304 |
Custom | 21543 | 18884 | 20442 |
TEXT (String) | 21816 | 17695 | 19882 |
Conclusion
Please note the Kryo provider is not in production quality. It just demonstrates how simple it is to enhance JAX-RS runtime to support new entity media type. However it is naive implementation of message body provider it provides very good performance. Kryo also support class registration and compression features that can also affect performance.
And finally, test applications work just with one instance of entity (Person). I guess Kryo could show it's power while working with more complex objects or with collection of them. And Kryo compression feature would be very strong in such case. Maybe I could prepare such test in couple of next months. ;-)