如何打包具有多个入口点的 Java 应用程序

How to package a Java app with multiple entrypoints

我一直在努力了解如何打包我们的应用程序,以便我们可以在一个 Docker 图像中 运行 多个命令。

我们的设置

bootstrap/Main 现在是一个 picocli entrypoint which registers other commands, i.e. HttpServer, CliCommandOne, CliCommandTwo, etc. These commands can be defined in other modules/packages. The app is packaged as a JAR with Gradle application plugin. By default, without parameters, HttpServer command will be used. It is possible, because in HttpServer command we can start an HTTP server explicitly (we're using https://jooby.io/ 框架。

Docker镜像部署到k8s。所以我们有一个服务器 运行ning,同时,我们可以 exec 进入容器和 运行 另一个 CLI 命令。

问题

我们想切换到另一个框架,例如Quarkus、Micronaut 或 Spring。看起来这些框架允许您启动 HTTP 服务器(或 WebSocket)或创建 CLI 命令,但是没有办法复制我们现在拥有的东西,即将多个命令打包在一个 JAR 中并能够在一张 Docker 图片。

我们想到的解决方案

我能想到的是Kafka正在使用的一种方法:据我所知,他们有一个JAR,然后使用很多sh脚本(https://github.com/apache/kafka/tree/trunk/bin) to run different classes https://github.com/apache/kafka/blob/trunk/bin/kafka-run-class.sh。这似乎喜欢为我们量身定做。

当然,我们可以使用 main 方法为每个 class 生成单独的 JAR,然后创建不同的 Docker 图像并以某种方式找到 运行他们。但就 Docker 图像而言,这似乎是一种开销。如果我们需要 20 个命令怎么办?

所以我正在寻找一种方法来打包应用程序以便拥有多个可执行文件 "commands"。我什至不确定,这是否是个好主意。很高兴听到可能的选择或最佳做法。

我在 Micronaut 中找到了一种方法。

控制器:

@Controller("/hello")
public class MyController {
    @Get(produces = MediaType.TEXT_PLAIN)
    public String hello() {
        return "hello";
    }
}

HTTP 服务器命令:

@Command(name = "http-server", description = "Starts HTTP server")
public class HttpApp implements Runnable {
    @Override
    public void run() {
        Micronaut.run(HttpApp.class);
    }
}

CLI 命令示例:

@Command(name = "cli-one", description = "test cli command one")
public class CliCommandOne implements Runnable {
    @Override
    public void run() {
        System.out.println("hello from CliCommandOne");
    }
}

主要class:

@Command(name = "main-command",
        description = "Description of a main command",
        mixinStandardHelpOptions = true,
        subcommands = {HttpApp.class, CliCommandOne.class})
public class Application implements Runnable {

    @Override
    public void run() {
        System.out.println("hello from CLI main command");
    }

    public static void main(String[] args) throws Exception {
        PicocliRunner.run(Application.class, args);
    }
}

获取所有可用命令的帮助(Gradle):

› gw run --args='--help'
11:37:20.928 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cli]
Usage: main-command [-hV] [COMMAND]
Description of a main command
  -h, --help      Show this help message and exit.
  -V, --version   Print version information and exit.
Commands:
  http-server  Starts HTTP server
  cli-one      test cli command one

运行 CliCommandOne:

› gw run --args='cli-one'
11:37:32.423 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cli]
hello from CliCommandOne

启动 HTTP 服务器:

gw run --args='http-server'
11:37:40.491 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cli]
11:37:41.263 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 307ms. Server Running: http://localhost:8081
<=========----> 75% EXECUTING [2s]