Eclipse Vert.x goes Native

I this blog post, I would like to give you a pre­view on na­tive image gen­er­a­tion of Vert.x ap­pli­ca­tions using GraalVM.

With GraalVM it is pos­si­ble to gen­er­ate na­tive ex­e­cuta­bles. These ex­e­cuta­bles can be di­rectly run with­out the need of an in­stalled JVM.

Benefits

  • The start up time is way faster. It is no longer re­quired to wait for the start up of the JVM. The ap­pli­ca­tion is usu­ally up and run­ning in a mat­ter of mil­lisec­onds.

  • Re­duced mem­ory foot­print. I mea­sured 40 MB mem­ory usage (RSS) for the Vert.x Web ap­pli­ca­tion which I’m going to show­case.

  • Smaller Con­tain­ers. No JVM means no over­head. All the needed parts are al­ready con­tained within the ex­e­cutable. This can be very ben­e­fi­cial when build­ing de­ploy­able con­tainer im­ages.

Demo Project

For the demo ap­pli­ca­tion I choose a very basic hello world Vert.x Web server.

package de.jotschi.examples;

import java.io.File;

import io.vertx.core.Vertx;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.logging.SLF4JLogDelegateFactory;
import io.vertx.ext.web.Router;

public class Runner {

  public static void main(String[] args) {
    // Use logback for logging
    File logbackFile = new File("config", "logback.xml");
    System.setProperty("logback.configurationFile", logbackFile.getAbsolutePath());
    System.setProperty(LoggerFactory.LOGGER_DELEGATE_FACTORY_CLASS_NAME, SLF4JLogDelegateFactory.class.getName());
    Logger log = LoggerFactory.getLogger(Runner.class);

    // Setup the http server
    log.info("Starting server for: http://localhost:8080/hello");
    Vertx vertx = Vertx.vertx();
    Router router = Router.router(vertx);

    router.route("/hello").handler(rc -> {
      log.info("Got hello request");
      rc.response().end("World");
    });

    vertx.createHttpServer()
      .requestHandler(router::accept)
      .listen(8080);

  }

}

GraalVM

GraalVM runs a sta­tic analy­sis on the gen­er­ated ap­pli­ca­tion in order to find the reach­able code. This process which is run within the Sub­strate VM will lead to the gen­er­a­tion of the na­tive image.

Limitations

Due to the na­ture of the sta­tic analy­sis Sub­strate VM also has some lim­i­ta­tions.

Dy­namic class load­ing and un­load­ing for ex­am­ple is not sup­ported be­cause this would in essence alter the avail­able code dur­ing run­time.

Re­flec­tion is only par­tially sup­ported and re­quires some man­ual steps which we will cover later on.

Patches / Workarounds

Work in progress

Next we need to apply some patches / workarounds. Keep in mind that na­tive image gen­er­a­tion is a fairly new topic and the these workarounds will hope­fully no longer be re­quired once the Sub­strate VM and Netty have bet­ter sup­port for each other.

I did not man­age to get na­tive epoll, kqueue and SSL in­te­gra­tion to work with na­tive im­ages. These parts are heav­ily op­ti­mized within Netty and use JNI to di­rectly ac­cess the OS fea­tures. Sub­strate VM sup­ports JNI and could in the­ory in­te­grate these na­tive li­braries.

I cre­ated a re­pro­ducer and an issue so hope­fully these prob­lems can be ad­dressed soon.

Vert.x Transport

First I needed to patch the io.vertx.core.net.impl.transport.Transport class in order to pre­vent the load­ing of EPoll and KQueue na­tive sup­port. Oth­er­wise Sub­strate VM will try to load these classes and fail.

public class Transport {
…
  /**
   * The native transport, it may be {@code null} or failed.
   */
  public static Transport nativeTransport() {
    // Patched: I remove the native transport discovery. 
    // The imports would be picked up by substrate 
    // and cause further issues. 
    return null;
  }
…
}

Netty SSL

Na­tive SSL sup­port is an­other prob­lem­atic area. I cre­ated a patched dummy io.netty.handler.ssl.ReferenceCountedOpenSslEngine class in order to pre­vent Sub­strate VM from dig­ging deeper into the SSL code of Netty.

Next we need to set up the re­flec­tion con­fig­u­ra­tion within reflectconfigs/netty.json.

Netty uses re­flec­tion to in­stan­ti­ate the socket chan­nels. This is done in the Re­flec­tiveChan­nelFac­tory. We need to tell Sub­strate VM how classes of type NioServer­Sock­etChan­nel and NioSock­etChan­nel can be in­stan­ti­ated.

[
  {
    "name" : "io.netty.channel.socket.nio.NioSocketChannel",
    "methods" : [
      { "name" : "<init>", "parameterTypes" : [] }
    ]
  },
  {
    "name" : "io.netty.channel.socket.nio.NioServerSocketChannel",
    "methods" : [
      { "name" : "<init>", "parameterTypes" : [] }
    ]
  }
]

If you want to learn more about the state of Netty and GraalVM I can rec­om­mend this GraalVM Blog­post by Co­drut Stancu.

Building

Fi­nally we can build our maven project to gen­er­ate a shaded jar.

mvn clean package

Next we need the GraalVM pack­age. You can down­load it from the GraalVM web­site.

We use the shaded jar as the input source for the native-image com­mand which will gen­er­ate the ex­e­cutable.

$GRAALVMDIR/bin/native-image \
 --verbose \
 --no-server \
 -Dio.netty.noUnsafe=true  \
 -H:ReflectionConfigurationFiles=./reflectconfigs/netty.json \
 -H:+ReportUnsupportedElementsAtRuntime \
 -Dfile.encoding=UTF-8 \
 -jar target/vertx-graalvm-native-image-test-0.0.1-SNAPSHOT.jar

Result

Fi­nally we end up with an 27 MB vertx-graalvm-native-image-test-0.0.1-SNAPSHOT ex­e­cutable which we can run.

$ ldd vertx-graalvm-native-image-test-0.0.1-SNAPSHOT 
  linux-vdso.so.1 (0x00007ffc65be8000)
  libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f8e892f0000)
  libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8e890d3000)
  libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f8e88eb9000)
  librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f8e88cb1000)
  libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1 (0x00007f8e88a79000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8e886da000)
  /lib64/ld-linux-x86-64.so.2 (0x00007f8e8afb7000)

Memory

/usr/bin/time -f "\nmaxRSS\t%MkB" java -jar target/vertx-graalvm-native-image-test-0.0.1-SNAPSHOT.jar 
/usr/bin/time -f "\nmaxRSS\t%MkB" ./vertx-graalvm-native-image-test-0.0.1-SNAPSHOT 
  • Na­tive Image: 40 MB
  • Java 10: 125 MB

The full project can be found on GitHub.

If you want to read more on the topic I can also rec­om­mend this ar­ti­cle by Re­nato Athay­des in which he demon­strates how to cre­ate a very small light weight low mem­ory ap­pli­ca­tion using GraalVM.

Thanks for read­ing. If you have any fur­ther ques­tions or feed­back don’t hes­i­tate to send me a tweet to @Jotschi.

Next post

Eclipse Vert.x 3.5.2

We have just released Vert.x 3.5.2, a bug fix release of Vert.x 3.5.x.

Read more
Previous post

Eclipse Vert.x RabbitMQ client gets a new consumer API!

In this blog post, we present the new consumer API of the RabbitMQ client that will be released with Eclipse Vert.x 3.6.0.

Read more
Related posts

Unit and Integration Tests

Let’s refresh our mind about what we developed so far in the introduction to vert.x series. We forgot an important task. We didn’t test the API.

Read more

Vert.x featuring Continuous Delivery with Jenkins and Ansible

This blog entry describes an approach to adopt Continuous Delivery for Vert.x applications using Jenkins and Ansible by taking advantage of the Jenkins Job DSL and Ansible plugins.

Read more

Checklist for Migrating from Vert.x 2.1.x to Vert.x 3 - Part One

So while upgrading our application, I thought I should note down all the changes that we had to do in the process. Since Vert.x 3 is a major upgrade from the previous version, with so many changes.

Read more