仅在Docker容器内使用XmlDecoder和Akka Stream的问题

2019年12月1日 22点热度 0条评论

我仅在Docker容器中运行的应用程序中在AkkaStream中使用XmlDecoder时遇到了问题。

错误描述

java.lang.ClassNotFoundException: com/example/xmldecoder/FileDto
Continuing ...
java.lang.ClassNotFoundException: com/example/xmldecoder/FileDto
Continuing ...
java.lang.NoSuchMethodException: <unbound>=XMLDecoder.new();
Continuing ...
java.lang.NoSuchMethodException: <unbound>=XMLDecoder.new();
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
java.lang.IllegalStateException: The outer element does not return value
Continuing ...
2019-05-22 09:42:29.145 ERROR 1 --- [onPool-worker-5] com.example.xmldecoder.FileReader        : Unexpected exception in load file, {} 
java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
    at java.desktop/java.beans.XMLDecoder.readObject(XMLDecoder.java:251) ~[na:na]
    at com.example.xmldecoder.FileReader.lambda$loadFile$0(XmlDecoderApplication.java:66) ~[classes!/:0.0.1-SNAPSHOT]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700) ~[na:na]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177) ~[na:na]


需要满足两个条件:

  • 该错误仅在Docker中发生,当您在非容器化主机上运行代码时,一切正常
  • 问题仅在您使用XmlDecoder时,使用BufferedReader逐行读取文件即可正常工作
  • 限制docker CPU(--cpus = 1)时不会发生错误
  • 当您使用ExecutorService而不是Akka Streams时,不会发生错误
  • 我尝试使用一些有助于解决JDK问题的docker标志(UseContainerSupportActiveProcessorCount),但是它对
  • 没有帮助


    代码

    可运行的示例可用
    here

    有问题的代码如下:

    @Slf4j
    @RequiredArgsConstructor
    class FileReader {
    
    private final ActorSystem system;
    private final ReadJob readJob;
    
    public NotUsed loadFiles() {
        List<String> paths = listFiles(readJob);
        return Source.from(paths)
                .via(Flow.of(String.class).mapAsync(5, p -> loadFile(p)))
                .to(Sink.foreach(System.out::println)).run(ActorMaterializer.create(system));
    }
    
    private CompletionStage<String> loadFile(String filePath) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                FileInputStream fis2 = new FileInputStream(filePath);
                BufferedInputStream bis2 = new BufferedInputStream(fis2);
                XMLDecoder xmlDecoder = new XMLDecoder(bis2);
                FileDto mb = (FileDto) xmlDecoder.readObject();
                log.info("Decoder: {}", mb);
                return mb.toString();
            } catch (Exception e) {
                log.error("Unexpected exception in load file, {}", e);
                throw new RuntimeException("Unexpected exception in load file", e);
            }
        });
    }
    
    private List<String> listFiles(ReadJob readJob) {
        File folder = new File(readJob.getHolderDirPath().toString());
        File[] listOfFiles = folder.listFiles();
        log.info(listOfFiles.toString());
        return Stream.of(listOfFiles).map(File::getAbsolutePath).collect(Collectors.toList());
    }
    

    }

    可以这样运行:

    @SpringBootApplication
    @EnableScheduling
    @Slf4j
    public class XmlDecoderApplication {
    
    private Path holderPath = Paths.get("opt", "files_to_load");
    
    public static void main(String[] args) {
        SpringApplication.run(XmlDecoderApplication.class, args);
    }
    
    @Scheduled(fixedDelay = 30000, initialDelay = 1000)
    public void readFiles() {
        FileReader reader = new FileReader(ActorSystem.create(), new ReadJob(holderPath));
        reader.loadFiles();
    }
    }
    

    我想根本原因是主机<-> docker <-> Java之间

    在此先感谢您的任何帮助

    解决方案如下:

    示例代码通过以下修改为我工作:替换行

    XMLDecoder xmlDecoder = new XMLDecoder(bis2);
    


    XMLDecoder xmlDecoder = new XMLDecoder(bis2, null, null, FileDto.class.getClassLoader());
    

    即有效地强制
    XMLDecoder使用用于加载所讨论类的精确类加载器。但至于为什么它只发生

    Docker中的

  • (如果--cpus设置为sth大于1
  • )

    带有Akka Streams的

  • 带有特定JDK版本的

  • –我只有一些(大部分)没有根据的猜测。