仅在 Docker 容器内的 XmlDecoder 和 Akka Stream 存在问题
Problem with XmlDecoder and Akka Stream only inside Docker container
我在 AkkaStream 中使用 XmlDecoder 时遇到了一个问题,仅在应用 运行ning 在 Docker 容器中。
错误描述
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[=11=](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 时,不会发生错误
- 我尝试使用一些 docker 标志来帮助解决 JDK 问题(
UseContainerSupport
、ActiveProcessorCount
),但没有帮助
代码
可用的可运行示例 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();
}
}
我想根本原因在 host <-> docker <-> java
之间
在此先感谢您对此的任何帮助
示例代码通过以下修改对我有效:替换行
XMLDecoder xmlDecoder = new XMLDecoder(bis2);
和
XMLDecoder xmlDecoder = new XMLDecoder(bis2, null, null, FileDto.class.getClassLoader());
即有效地强制 XMLDecoder
使用精确的 classloader 来加载有问题的 class。但至于为什么只出现
- 在Docker
- 如果
--cpus
设置为大于 1 的某个值
- 使用 Akka Streams
- 具有特定 JDK 版本
– 我只有一些(大部分)没有根据的猜测。
我在 AkkaStream 中使用 XmlDecoder 时遇到了一个问题,仅在应用 运行ning 在 Docker 容器中。
错误描述
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[=11=](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 时,不会发生错误
- 我尝试使用一些 docker 标志来帮助解决 JDK 问题(
UseContainerSupport
、ActiveProcessorCount
),但没有帮助
代码
可用的可运行示例 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();
}
}
我想根本原因在 host <-> docker <-> java
之间在此先感谢您对此的任何帮助
示例代码通过以下修改对我有效:替换行
XMLDecoder xmlDecoder = new XMLDecoder(bis2);
和
XMLDecoder xmlDecoder = new XMLDecoder(bis2, null, null, FileDto.class.getClassLoader());
即有效地强制 XMLDecoder
使用精确的 classloader 来加载有问题的 class。但至于为什么只出现
- 在Docker
- 如果
--cpus
设置为大于 1 的某个值 - 使用 Akka Streams
- 具有特定 JDK 版本
– 我只有一些(大部分)没有根据的猜测。