kafka-connect-tools
kafka-connect-tools copied to clipboard
Compile to native binaries using GraalVM Native Image
It should be possible to get rid of the JVM overhead using the GraalVM Native Image mechanism. I will take a look at this later, but have you folks already experimented with this route before?
I got this working with some tinkering, after needing to generate some configuration json
for GraalVM to cope with the reflection used in http libs. Writeup coming soon but here's the results for the pure startup time:
MBPW:kafka-connect-tools bshilov$ time bin/connect-cli ps
java.lang.Exception: Error: the Kafka Connect API returned status code 401
at com.datamountaineer.connect.tools.RestKafkaConnectApi.non2xxException(RestKafkaConnectApi.scala:124)
at com.datamountaineer.connect.tools.RestKafkaConnectApi.com$datamountaineer$connect$tools$RestKafkaConnectApi$$req(RestKafkaConnectApi.scala:141)
at com.datamountaineer.connect.tools.RestKafkaConnectApi$$anonfun$activeConnectorNames$1.apply(RestKafkaConnectApi.scala:153)
at com.datamountaineer.connect.tools.RestKafkaConnectApi$$anonfun$activeConnectorNames$1.apply(RestKafkaConnectApi.scala:152)
at scala.util.Try$.apply(Try.scala:192)
at com.datamountaineer.connect.tools.RestKafkaConnectApi.activeConnectorNames(RestKafkaConnectApi.scala:152)
at com.datamountaineer.connect.tools.ExecuteCommand$.apply(Cli.scala:66)
at com.datamountaineer.connect.tools.Cli$.main(Cli.scala:209)
at com.datamountaineer.connect.tools.Cli.main(Cli.scala)
real 0m0.748s
user 0m1.010s
sys 0m0.090s
MBPW:kafka-connect-tools bshilov$ time ./kafka-connect-cli-02 ps
java.lang.Exception: Error: the Kafka Connect API returned status code 401
at com.datamountaineer.connect.tools.RestKafkaConnectApi.non2xxException(RestKafkaConnectApi.scala:124)
at com.datamountaineer.connect.tools.RestKafkaConnectApi.com$datamountaineer$connect$tools$RestKafkaConnectApi$$req(RestKafkaConnectApi.scala:141)
at com.datamountaineer.connect.tools.RestKafkaConnectApi$$anonfun$activeConnectorNames$1.apply(RestKafkaConnectApi.scala:153)
at com.datamountaineer.connect.tools.RestKafkaConnectApi$$anonfun$activeConnectorNames$1.apply(RestKafkaConnectApi.scala:152)
at scala.util.Try$.apply(Try.scala:192)
at com.datamountaineer.connect.tools.RestKafkaConnectApi.activeConnectorNames(RestKafkaConnectApi.scala:152)
at com.datamountaineer.connect.tools.ExecuteCommand$.apply(Cli.scala:66)
at com.datamountaineer.connect.tools.Cli$.main(Cli.scala:209)
at com.datamountaineer.connect.tools.Cli.main(Cli.scala)
real 0m0.187s
user 0m0.025s
sys 0m0.022s
First is the master jar, second is the GraalVM binary.
This is purely via the executables, no Gradle yet.
Right so to get to this result you need to install GraalVM and add it to your path:
brew cask install graalvm/tap/graalvm-ce-java8
export PATH=/Library/Java/JavaVirtualMachines/graalvm-ce-java8-20.0.0/Contents/Home/bin:"$PATH"
And then install the Native Image plugin via:
gu install native-image
The next important step is to make sure you have the following variables set to JDK 1.8 Home and Scala 2.11 Home, else the GraalVM image produced will fail with very odd errors. I manage everything via homebrew so the below are my own homebrew installation paths, replace as necessary:
export JAVA_HOME='/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home'
export SCALA_HOME='/usr/local/Cellar/[email protected]/2.11.12'
And for convenience define the home of GraalVM as a var too:
export GRAAL_HOME='/Library/Java/JavaVirtualMachines/graalvm-ce-java8-20.0.0/Contents/Home'
Now we need to generate some files to deal with the usage of reflection in our dependencies. This is done via:
mkdir -p META-INF/native-image
$GRAAL_HOME/bin/java -agentlib:native-image-agent=config-output-dir=META-INF/native-image -jar bin/connect-cli ps
This will actually run the fat jar the current master creates and profile which libraries use reflection and write the results to META-INF/native-image
. We pass them to the next step, the actual native image builder:
native-image -H:ReflectionConfigurationFiles=META-INF/native-image/reflect-config.json -H:JNIConfigurationFiles=META-INF/native-image/jni-config.json -H:ResourceConfigurationFiles=META-INF/native-image/resource-config.json -H:EnableURLProtocols=https -jar build/libs/kafka-connect-cli-1.0.8-all.jar kafka-connect-cli-graal
Now this should generate a perfectly functional kafka-connect-cli-graal
executable.
Hi @muscovitebob - out of curiosity what is the end result ?
i.e. "an executable that is x KBytes rather than y"
Do you get executables for different linux / mac / win etc ? Share a bit insight as i haven't looked into Graal for a while - thx
Hi @Antwnis!
You create different, statically linked binaries for each target platform (Mac, Linux, Win). No JVM installation needed.
Instead of manually creating the reflect-config.json
file, I'd suggest using the tracing agent, which can do this automatically (by tracking class-loading / reflection usage at runtime, e.g. during test runs).