问题描述

我有一个来自org.javamoney:moneta:1.3的gradle依赖的Java项目。

我也有两个Kubernetes集群。我使用docker-container部署Java应用程序。

当我在第一个 Kubernetes集群中部署我的应用程序时,一切都很好。但是当我在第二个 Kubernetes集群中部署我的应用程序(相同的docker-container)时,出现以下错误:

javax.money.MonetaryException: No MonetaryAmountsSingletonSpi loaded.
    at javax.money.Monetary.lambda$getDefaultAmountFactory$13(Monetary.java:291)
    at java.base/java.util.Optional.orElseThrow(Optional.java:408)
    at javax.money.Monetary.getDefaultAmountFactory(Monetary.java:291)

它显示在以下代码中:

MonetaryAmount amount = javax.money.Monetary.getDefaultAmountFactory()
    .setCurrency("USD")
    .setNumber(1L)
    .create();

软件版本
  • Moneta:1.3
  • Gradle:6.0.1
  • 基本docker-image:openjdk:11.0.7-jdk-slim
  • Spring 启动:2.2.7.RELEASE
  • Kubernetes(两个集群上的版本相同):Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.3", GitCommit:"2d3c76f9091b6bec110a5e63777c332469e0cba2", GitTreeState:"clean", BuildDate:"2019-08-19T11:05:50Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}
  • Java:java -versionopenjdk version "11.0.7" 2020-04-14OpenJDK Runtime Environment 18.9 (build 11.0.7+10)OpenJDK 64-Bit Server VM 18.9 (build 11.0.7+10, mixed mode)

  • 我尝试过的

    声明gradle-dependency不同

    我找到了this question,这给了我一个尝试以某种不同的方式声明gradle-dependency的想法。我努力了:
  • implementation 'org.javamoney:moneta:1.3'
  • compile group: 'org.javamoney', name: 'moneta', version: '1.3', ext: 'pom'
  • compile 'org.javamoney:moneta:1.3'
  • runtimeOnly 'org.javamoney:moneta:1.3'

  • 不幸的是,它没有给出任何积极的结果。

    Moneta的复制粘贴服务加载程序配置

    this comment中所述,我尝试将服务加载程序配置from Moneta复制到以下项目目录:src/main/resources/META-INF/services

    不幸的是,它没有帮助。

    没有 Spring 的初始化自定义货币

    我已经尝试过只在Main-class中做到这一点,但是并不能解决问题。

    问题
  • 此问题的根本原因是什么?
  • 该问题的正确解决方案是什么?
  • 最佳答案

    TL; DR

    问题出在Java 11中的并发moneta SPI初始化中。

    问题方案

    可以通过将MonetaryAmountFactory提取到spring-bean并将其注入(inject)到需要的位置来解决该问题:

    @Bean
    public MonetaryAmountFactory<?> money() {
        return Monetary.getDefaultAmountFactory();
    }
    
    
    @Component
    @RequiredArgsConstructor
    public static class Runner implements CommandLineRunner {
    
        private final MonetaryAmountFactory<?> amountFactory;
    
        @Override
        public void run(String... args) {
            var monetaryAmount = this.amountFactory
                .setCurrency("EUR")
                .setNumber(1)
                .create();
    
            System.out.println("monetaryAmount = " + monetaryAmount);
        }
    }
    

    而不是直接使用此工厂:

    public static class Runner implements CommandLineRunner {
    
        @Override
        public void run(String... args) {
            var monetaryAmount = Monetary.getDefaultAmountFactory()
                .setCurrency("EUR")
                .setNumber(1)
                .create();
    
            System.out.println("monetaryAmount = " + monetaryAmount);
        }
    }
    

    为什么在Kubernetes集群上出现问题?

    我发现上述Kubernetes集群中存在不同的resource limit configuration

    集群除外:
    Limits:
      cpu:     6
      memory:  20G
    Requests:
      cpu:      3
      memory:   20G
    

    集群无一异常(exception):
    Limits:
      cpu:     2
      memory:  2G
    Requests:
      cpu:      2
      memory:   128Mi
    

    似乎具有更多资源的群集为并发moneta初始化发生提供了更多机会。

    最小的可复制示例

    最小的可重现示例可以在this github-repository中找到。

    值得一提的是,该错误未在Java 8上重现。

    08-07 17:06