[spring-projects/spring-boot]尝试使用 SpringIterableConfigurationPropertySource 提高性能

2024-05-14 500 views
1

运行具有 >10k 外部属性的项目。

看来Spring处理所有属性绑定花费了很长时间,并且还包含一个深堆栈。

我不确定如何进一步进行此操作或优化,或者这是否只是需要从性能角度进行处理。

图像

附飞行录音。

飞行记录.zip

春季启动2.3.12

回答

5

为什么要这么做?

5

@pintomau 您可能想升级到 Spring Boot 2.5 或 2.6,看看 #17400 是否对您的情况有任何影响。我怀疑可能不会,因为我们在其他项目中还没有遇到过接近这个数量的外部属性。如果没有帮助,您能否提供一个示例应用程序来准确显示您正在做什么。

6

我们很难更新,因为我们对 Spring Cloud 有一些硬依赖,这些依赖已被删除或需要适应。

否则需要一些时间来重现这一点。

为什么要这么做?

多租户平台,数十个租户,每个租户有数百种不同的配置。

谢谢。

2

运行具有 >10k 外部属性的项目。

我的朋友,拥有如此多的外部属性是糟糕的设计。您应该避免在应用程序启动时加载它们。我建议在运行时,在第一次功能执行时执行此操作;你的设计确实出了问题:这就像用 SpringBoot 属性替换数据库数据一样。

7

如果没有更多背景信息,我认为没有人能够将 @pintomau 的应用程序的设计描述为糟糕。在他们对替代方法的一些建议表示兴趣或提供有关导致当前设计的目标和约束的更多详细信息之前,我们不要急于做出负面判断。

我们上面说过,一旦我们有了重现问题的样本,我们很乐意看看并看看我们可以采取什么措施来加快速度。加快处理数千个属性也可能有利于只有数十或数百个属性的应用程序。

3

如果您希望我们查看此问题,请提供所需的信息。如果在接下来的 7 天内未提供信息,此问题将被关闭。

3

由于缺乏所需的反馈而关闭。如果您希望我们查看此问题,请提供所需的信息,我们将重新打开该问题。

5

我破解了一个小而简单的示例来展示该问题:https://github.com/rezekdan/boottimes

TLDR:10k 个配置绑定,每个绑定 4 个属性。为这个简单的应用程序绑定 40k 属性需要两分钟才能在我的计算机上启动。除了绑定之外,这个示例没有做任何其他事情,只是按照文档的预期使用 spring 。我向 @pintomau 确认,分析器显示的示例应用程序的结果与原始应用程序案例的结果非常相似。

1

谢谢,@rezekdan。我们来看看。

7

这是因为我们的老朋友SpringIterableConfigurationPropertySource每次访问源时都必须创建映射。可以加快速度的一件事是,如果 Spring Cloud 添加的属性源被标记为不可变。

将以下 hack 应用于示例:

@SpringBootApplication
@EnableConfigurationProperties({ BootTimesConfiguration.class })
public class BoottimesClientApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(BoottimesClientApplication.class);
        application.addListeners(new HackImmutable());
        application.run(args);
    }

    private static class HackImmutable implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

        @Override
        public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
            Field field = ReflectionUtils.findField(OriginTrackedMapPropertySource.class, "immutable");
            field.setAccessible(true);
            ConfigurableEnvironment environment = event.getEnvironment();
            for (PropertySource<?> source : environment.getPropertySources()) {
                if (source.getName().startsWith("configserver:")) {
                    ReflectionUtils.setField(field, source, true);
                }
            }
        }

    }

}

在我的本地笔记本电脑上,启动时间从 38.455 秒下降到 4.498 秒。

我提出了https://github.com/spring-cloud/spring-cloud-config/issues/2032来看看 Spring Cloud 团队是否可以改变他们这边的事情。

7

我想保留这个问题,看看是否有其他方法可以改进。

2

嗨@philwebb。

为了扩展这个问题,我们在项目中进行了尝试,但我们注意到 @rezekdan 开发的 PoC 与我们自己的 PoC 之间存在一些差异。

我们使用 Redis 作为属性数据库。我们注意到,redis 属性不受您提供的 hack 的影响(也许我们可以使用不同的事件?)。

无论如何,我们已经阴影OriginTrackedMapPropertySource#isImmutable总是返回 true,但我们仍然有另一个问题。

Spring云配置客户端似乎将Redis'包装OriginTrackedMapPropertySourceBootstrapPropertySource.然后,当然,source instanceof OriginLookupinSpringIterableConfigurationPropertySource返回 false,这意味着isImmutable不会被调用。

我们再次诉诸阴影SpringIterableConfigurationPropertySource并添加了一些行来考虑此包装器:

EnumerablePropertySource<?> source = getPropertySource();
    if (source instanceof BootstrapPropertySource) {
      source = (EnumerablePropertySource<?>) ((BootstrapPropertySource) source).getDelegate();
    }
    if (source instanceof OriginLookup) {
      return ((OriginLookup<?>) source).isImmutable();
    }
    if (StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME.equals(source.getName())) {
      return source.getSource() == System.getenv();
    }
    return false;

正如您提到的,这大大提高了性能。我不确定是否有任何副作用。

当然,我们也不想诉诸阴影。也许你有什么想法?

谢谢!

0

Spring Boot 2.7 也存在同样的问题。

我们正在使用ConfigurationProperties功能来加载由大约 30k 行 yaml 组成的应用程序配置。它是从 Excel 文件动态生成的搜索字段列表。

searchFields:
  - searchNames:
      - foo
    elasticFields:
      - elasticName: foo
        converter: defaultSearchFieldConverter
        fieldType: KEYWORD

该方法受益于开箱即用的 java bean 验证(字段使用 javax 验证进行注释)和本机 Spring 转换器。有converter一颗春豆。

加载/绑定大约需要 30 秒。

通过在 Spring 功能之外加载 yaml 肯定会更快,但我们必须进行验证并实现一些转换器。

1

@nithril 30k 行是相当极端的,确实让我想知道你是否最好使用不同的方法。也就是说,我们仍然想尝试改进这里的事情。为此,您能否分享一个示例(如果需要的话可以使用合成数据)来重现缓慢的绑定性能?