编译为本机映像的SparkJava不起作用

编译为本机映像的SparkJava不起作用,java,spark-java,graalvm-native-image,Java,Spark Java,Graalvm Native Image,我正在尝试编译一个简单的java应用程序,它基于一个本地映像。应用程序公开一个GET/healthcheck端点,该端点始终返回text/plain:OK 项目中只有两个依赖项: dependencies { implementation 'com.sparkjava:spark-core:2.9.1' implementation 'org.slf4j:slf4j-simple:1.7.21' } 我通过GraalVM运行fatjar创建了本机映像配置: java -agentlib

我正在尝试编译一个简单的java应用程序,它基于一个本地映像。应用程序公开一个
GET/healthcheck
端点,该端点始终返回text/plain:
OK

项目中只有两个依赖项:

dependencies {
  implementation 'com.sparkjava:spark-core:2.9.1'
  implementation 'org.slf4j:slf4j-simple:1.7.21'
}
我通过GraalVM运行fatjar创建了本机映像配置:

java -agentlib:native-image-agent=config-output-dir=native-image/ -jar javaspark-native-1.0-SNAPSHOT-fatjar.jar
重要的是,我正在对端点进行REST调用,以确保代理可以检查代码中必要的分支。生成的*.json文件随后被放入
META-INF/native image
中,因此由
native image
自动拾取

然后,我使用多级docker build编译fatjar,并将jar编译为本机映像:

FROM ubuntu:18.04
RUN apt-get update && apt-get install -y gcc zlib1g-dev wget

RUN wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-19.3.0.2/graalvm-ce-java11-linux-amd64-19.3.0.2.tar.gz
RUN tar -vzxf graalvm-ce-java11-linux-amd64-19.3.0.2.tar.gz
ENV PATH /graalvm-ce-java11-19.3.0.2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
RUN gu install native-image

WORKDIR /graalvm-demo
COPY . /graalvm-demo

RUN ./gradlew clean fatJar
RUN native-image --verbose --enable-http -H:+ReportUnsupportedElementsAtRuntime --no-fallback -jar /graalvm-demo/build/libs/javaspark-native-1.0-SNAPSHOT-fatjar.jar

FROM adoptopenjdk/openjdk11:x86_64-alpine-jdk-11.0.3_7-slim
WORKDIR /graalvm-demo
COPY --from=0 /graalvm-demo/javaspark-native-1.0-SNAPSHOT-fatjar .
RUN apk --update --no-cache add \
  curl \
  tar \
  && rm -rf /var/cache/apk/*

EXPOSE 8080
CMD ./javaspark-native-1.0-SNAPSHOT-fatjar
通过以下方式触发docker生成时:

docker build . -t app
一切看起来都很好-映像已创建,启动时控制台不会报告任何问题:

[Thread-0] INFO org.eclipse.jetty.util.log - Logging initialized @1ms to org.eclipse.jetty.util.log.Slf4jLog
[Thread-0] INFO spark.embeddedserver.jetty.EmbeddedJettyServer - == Spark has ignited ...
[Thread-0] INFO spark.embeddedserver.jetty.EmbeddedJettyServer - >> Listening on 0.0.0.0:8080
[Thread-0] INFO org.eclipse.jetty.server.Server - jetty-9.4.z-SNAPSHOT; built: 2019-04-29T20:42:08.989Z; git: e1bc35120a6617ee3df052294e433f3a25ce7097; jvm 11.0.5
[Thread-0] INFO org.eclipse.jetty.server.session - DefaultSessionIdManager workerName=node0
[Thread-0] INFO org.eclipse.jetty.server.session - No SessionScavenger set, using defaults
[Thread-0] INFO org.eclipse.jetty.server.session - node0 Scavenging every 600000ms
[Thread-0] INFO org.eclipse.jetty.server.AbstractConnector - Started ServerConnector@6260ba9e{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
[Thread-0] INFO org.eclipse.jetty.server.Server - Started @4ms
但是,当我尝试访问端点时,它不起作用:

[qtp1802776325-8] WARN org.eclipse.jetty.io.ManagedSelector - java.lang.ExceptionInInitializerError
[qtp1802776325-10] WARN org.eclipse.jetty.io.ManagedSelector - java.lang.NoClassDefFoundError: Could not initialize class org.eclipse.jetty.server.HttpOutput
有人知道我做错了什么吗


存储库中演示该问题的提交:

自GraalVM 19.0.x()以来的结果:

在本机映像中初始化类的方式发生了变化。现在,我们默认在运行时初始化应用程序类

这就解释了为什么到目前为止我看到的所有教程都没有提到我的问题中描述的问题——它们都使用早期版本的GraalVM,其中静态类默认在构建时初始化。采用同样的方法可以解决这个问题。与:

RUN native-image \
    -H:+ReportUnsupportedElementsAtRuntime \
    -H:+TraceClassInitialization \
    --verbose \
    --enable-http \
    --static \
    --no-fallback \
    --initialize-at-build-time=org.eclipse.jetty,org.slf4j,javax.servlet,org.sparkjava \
    -jar /sparkjava/build/libs/javaspark-native-1.0-SNAPSHOT-fatjar.jar
一切正常。对于alpine base image,生成的docker图像为26M:

REPOSITORY   TAG    IMAGE ID      CREATED              SIZE
ni7          latest dcb94dbfb6ad  About a minute ago   26MB

完整的解决方案可以在这里看到: