Example project with simple Akka HTTP server compiled with GraalVM native-image.
- SBT
- GraalVM
native-image
fromGRAAL_HOME/bin
inPATH
Suggested environment variables:
export GRAAL_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home
export PATH=$PATH:${GRAAL_HOME}/bin
gu install native-image
sbt graalvm-native-image:packageBin
It might take a few minutes to compile.
# 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.
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.
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.
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.
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.
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.
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.
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
.
SubstrateVM does not support Java serialization yet so anything that depends on
akka.serialization.JavaSerializer
will not work with native-image
.
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.