Home

Awesome

Akka HTTP + GraalVM native

Example project with simple Akka HTTP server compiled with GraalVM native-image.

Pre-requisites

Suggested environment variables:

export GRAAL_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home
export PATH=$PATH:${GRAAL_HOME}/bin

Install native-image:

gu install native-image

Compiling

sbt graalvm-native-image:packageBin

It might take a few minutes to compile.

Running

# MacOS:
./target/graalvm-native-image/akka-graal-native -Djava.library.path=${GRAAL_HOME}/jre/lib

# Linux:
./target/graalvm-native-image/akka-graal-native -Djava.library.path=${GRAAL_HOME}/jre/lib/amd64

Because the project is compiled with Java Crypto enabled for the native image (to support HTTPS) java.library.path system property must be set at runtime to point to a directory where the dynamic library for the SunEC provider is located.

After the server starts you can access http://localhost:8086/graal-hp-size which will make an HTTPS request to the GraalVM home page using Akka HTTP client and return the size of the response.

How it works

Most of the Akka-specific configuration for native-image is provided by akka-graal-config repository which publishes a set of jar artifacts that contain the necessary configuration resources for native-image to compile Akka modules. Just having these jars in the classpath is enough for native-image to pick up this configuration. See this blog post for more details on how that mechanism works.

Reflection configuration

See SubstrateVM docs for details.

Configuration for Akka itself is provided by akka-graal-config dependencies. This repo contains only reflection configuration to get java.util.logging working.

Note however that reflective access to context and self fields must be configured for every actor that is monitored with context.watch (observed empirically). Otherwise you'll get an error from Akka's machinery.

HTTPS

Passing the --enable-url-protocols=https option to native-image enables JCE features. This configuration option is enabled by configuration from graal-akka-http dependency.

Affinity Pool

akka.dispatch.affinity.AffinityPool is using MethodHandles which causes errors like this one during native image build:

Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: Invoke with MethodHandle argument could not be reduced to at most a single call: java.lang.invoke.MethodHandle.bindTo(Object)

The workaround is to initialize affected classes (and we actually do this for the whole classpath) at build time using the --initalize-at-build-time option.

Lightbend Config

Static initializers of com.typesafe.config.impl.ConfigImpl$EnvVariablesHolder and com.typesafe.config.impl.ConfigImpl$SystemPropertiesHolder need to be run at runtime using the --initialize-at-run-time option. Otherwise the environment from image build time will be baked in to the configuration.

Akka Scheduler and sun.misc.Unsafe

To make the default Akka scheduler work with SubstrateVM it is necessary to recalculate the field offset that it uses with sun.misc.Unsafe. This is done by using SubstrateVM API in AkkaSubstitutions class from graal-akka-actor dependency.

For more details see the section about Unsafe in this blog post.

Note that this substitution is only necessary with Scala 2.12. Curiously with Scala 2.13 native-image can make the substitution itself automatically. Probably due to some difference in the emitted bytecode.

MethodHandle in scala.runtime.Statics

In Scala 2.13 a MethodHandle is used in Statics.releaseFence() to invoke either java.lang.invoke.VarHandle.releaseFence() if running in Java 9 VM or sun.misc.Unsafe.storeFence() if on Java 8. As noted above, MethodHandles are a problem with native-image but since GraalVM is currently based on Java 8 Statics.releaseFence() can be substituted to always call Unsafe without using MethodHandle. This is done by ScalaSubstitutions present in Scala 2.13 version of graal-akka-actor.

Serialization

SubstrateVM does not support Java serialization yet so anything that depends on akka.serialization.JavaSerializer will not work with native-image.

Logging

It is currently not easy to get Logback working because of its Groovy dependencies and incomplete classpath problems with native-image so java.util.logging is used instead.