0

我有 10 多个 Spring Boot 应用程序,它们可以构建为 war 文件并部署在应用程序服务器中,或者单独运行。每个应用程序都包含业务运营所需的唯一代码、功能和 RESTful 服务。

我的包结构如下:

WebServices (Gradle project)
|---A (Gradle project)
|---B (Gradle project)
|---C (Gradle project)

服务 A、B 和 C 都被打包并作为战争运行。

但是,我还想提供启动一个包含所有服务的“大型”服务器的选项,而不必单独启动每个应用程序或让主应用程序服务器陷入大型 Spring Boot 战争。

理想情况下,这将通过另一个 Spring Boot 应用程序使用 ComponentScan 来包含来自所有其他服务的功能。我打算让 Spring Boot 应用程序X引用服务ABC中的功能。

问题在于上下文。我的每个服务在通过应用程序服务器启动时,都会根据 war 文件的名称自动分配一个上下文。例如,如果我在应用程序A中具有安全功能,因为它包含我登录时使用的敏感信息:

/安全/登录

其中security是战争文件 (security.war) 的名称。

为了在独立运行时弥补这一点,我为上下文设置了一个应用程序属性以匹配 war 文件名server.servlet.context-path: /security。这使我可以维护使用我的任何一种部署方法部署的相同端点天气。

现在,当我使用组件扫描启动引用项目ABC的服务器X时,我失去了应用程序A中的安全上下文,我的端点现在被访问为:@ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"})

/登录

应用程序ABC之间没有区别。

所以,我的问题是如何根据被扫描的组件维护单个上下文,甚至基于上下文的路由?

4

2 回答 2

1

由于弹簧靴并不是真正按照您的要求设计的,因此您必须非常有创意。以下是一些方向:

选项1

只要您在同一台服务器上部署了许多战争,contextPaths 就会有所不同,您可以根据实际服务器使用它们,但它们必须不同,以便 Web 服务器可以以某种方式区分它们。考虑到这一点,您可以运行您正在尝试执行的操作,然后定义一个代理(Zuul、ha-proxy 或其他),该代理会仔细地将每个可能的 url 映射到适当的服务器。

选项 2

与 1 几乎相同,但您可以使用 docker-compose(甚至 kubernetes)并将 Spring Boot 应用程序作为不同的容器运行。如果您想在 CI 或开发中运行,这可能是一个更好的解决方案(docker compose 将根据创建的 war 文件的位置为您构建所有内容,无需移动到 Web 服务器、部署服务器等)。只是说明这是一个选项。

选项 3

如果您真的希望它们都在同一个内容路径中。创建一个 gradle 插件,将战争/罐子合并为一个。脂肪罐风格。请注意,spring boot 应用程序可能有不同的布局(肯定是在 Jars 的情况下),因此实现可能应该执行以下操作:

步骤1

该插件假定模块 A、B、C 已构建。现在我没有使用 Gradle 的经验,但是在 Maven 中,您可以转到目标目录(我相信 gradle 有一个用于相同目的的构建目录)并提取 war/jar 的内容(类、资源、依赖项)进入另一个文件夹,比如项目 A,然后对项目 B 执行相同操作,依此类推。依赖关系可能会发生冲突(不同的版本,因此您必须确保所有应用程序都使用相同版本的依赖关系来消除问题),类不应该发生冲突,因为它们应该驻留在不同的包中(com.package.a而不是com.package.b)。

第2步

在步骤“a”之后,您必须将所有内容打包到您选择的工件中,这应该仍然是一个 Spring Boot 项目。如果您选择 JAR,则必须了解它的构建方式。我无法对 WAR 发表评论,也没有与 Spring Boot 中的人合作过。

选项 4

与选项 3 几乎相同,但步骤 1 是在它构建的实际项目之一(如项目 A、B 等)时完成的。该插件会将生产类、依赖项或其他任何内容复制到某个预定义的文件夹中。该插件将安装在每个模块中,因此当它运行时,它将“贡献”到包含所有必需资源的文件夹中。步骤 2 将与选项 3 中的相同。

更新

根据您的评论,请考虑 Jars 和选项 1 / 2。Jars 是在 Spring Boot 应用程序中工作的推荐方式,Wars 应该用于维护 Web 服务器并且不想进行“Ops”更改的老式组织。

现在,如果端口是一个问题,如果您使用容器/kubernetes 编排,还有一个选项可以为每个服务分配一个虚拟主机名,这样所有服务都可以在同一个端口上访问,但使用虚拟主机名。代理选项将允许所有人使用相同的端口和主机

于 2019-12-05T05:24:11.223 回答
0

我以另一种方式解决了这个问题。

RequestMapping("${serviceContext}")我在每个单独的控制器上都设置了默认值。每个控制器的服务更改的地方。即aContextbContextcContext

在我已经被上下文绑定的环境中,在应用程序服务器中并且通过 application.yml 独立,这个属性没有设置并且导致已经存在的绑定。

在我的捆绑应用程序中,我能够保持对@ComponentScan(basePackages = {"com.package.a.*", "com.package.b.*", "com.package.c.*"}). 然后,更改变为在应用程序启动期间添加其他属性。

用于绑定上下文的属性

Properties properties = new Properties();
properties.put("aContext", "/security");
properties.put("bContext", "/b");
properties.put("cContext", "/c");

setDefaultProperties在运行应用程序之前,我将它添加到 Spring中。然后,当我将每个作为一个包启动时,我得到了预期的上下文绑定。

于 2019-12-15T02:32:10.890 回答