Docker 容器阻塞中的 Apache Batik 转码器
Apache Batik Transcoder inside Docker Container Blocking
我们在 docker 容器中 运行 安装一个 Spring 应用程序。我们的应用程序可以获取 SVG 文件并将它们运行转换为 PDF 格式以嵌入到 PDF 中。
该应用程序按预期在 osx 和 t运行 代码上正常工作。然而,当 运行 来自具有不同文件系统的 docker 容器时,t运行scoder 会卡住并在一些奇怪的递归文件搜索循环中击败 cpu。
java.lang.Thread.State: RUNNABLE
at java.io.UnixFileSystem.getBooleanAttributes0(Native Method)
at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242)
at java.io.File.isFile(File.java:882)
at org.apache.commons.io.filefilter.FileFileFilter.accept(FileFileFilter.java:59)
at org.apache.commons.io.filefilter.AndFileFilter.accept(AndFileFilter.java:122)
at org.apache.commons.io.filefilter.AndFileFilter.accept(AndFileFilter.java:122)
at org.apache.commons.io.filefilter.OrFileFilter.accept(OrFileFilter.java:118)
at java.io.File.listFiles(File.java:1291)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:357)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364
下面是 运行 PDFT运行scoder 线程的堆栈跟踪。 Walk 被递归调用了一段时间,然后最终调用了 getBooleanAttributes0 并且一切都阻塞了。
经过进一步研究,我们发现我们可以仔细查看 strace
命令发生的情况,发现系统本质上是在无限循环中发送以下垃圾邮件。
stat("/./sys/devices/pci0000:00/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/pcspkr/input/input1/subsystem/input0/subsystem/input0/uniq", {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0 <0.000224>
我们似乎在统计调用中被阻塞或挂起。但是我们现在已经对系统调用进行了如此深入的研究,以至于很难调试。有人有什么想法吗?
我的项目遇到了同样的问题。我通过将 batik 降级到 1.7 版本来解决它。
我希望这对你有用。
尝试将参数“-Duser.dir=/%CATALINA_HOME/”添加到您的 CATALINA_OPTS。我在我的centos服务器上遇到了同样的问题。
我遇到了同样的问题。
通过在 DockerFile 中设置 WORKDIR 变量解决了这个问题。
我将它设置为我的部署目录,我在其中复制 spring jar 文件。即:
WORKDIR ${DEPLOYMENT_DIR}
在 pom 中使用最新的蜡染库
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-all</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.2</version>
</dependency>
我遇到了同样的错误。在尝试了很多方法来修复它之后,我得出的结论是您在 Mac OS X 上可以使用字体,而您的(无头)Docker 容器 OS没有字体。在到处搜索字体时,转码器并没有优雅地失败。我通过强制代码转换器 use the default fonts (并且不自动查找其他字体)解决了这个问题,如下所示:
...
PDFTranscoder transcoder = new PDFTranscoder();
transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);
...
transcoder.transcode(transcoderInput, transcoderOutput);
...
请注意,当它遇到 14 种字体之外的一种时,当然会退回到其已知字体的缺点。我试过一些方法来解决这个问题,但到目前为止没有成功。
我希望这对某人有所帮助。
我遇到了同样的问题并在我的案例中解决了。这个线程帮助了很多。现在我想将所有部分放在一起 - 也许也适用于遇到此问题的其他人。
这是因为您启动 Java 应用程序的目录。我认识到在以下情况下会出现此问题:
- Java 应用程序在文件系统根目录中启动。
- Apache FOP 中启用了自动扫描字体。
我在 中找到了类似的 post。 Fyodor Sherstobitov 的解释听起来很有道理。
Apache FOP 使用您的 Java 应用程序的工作目录来扫描字体。在这种情况下,这是文件系统根目录。因此将扫描整个文件系统。
以下代码复制自PDFDocumentGraphics2DConfigurator
。它表明使用了 new File(".").getAbsoluteFile().toURI()
- 这是工作目录 resp。 Java 应用程序启动的目录。
/**
* Creates the {@link FontInfo} instance for the given configuration.
* @param cfg the configuration
* @param useComplexScriptFeatures true if complex script features enabled
* @return the font collection
* @throws FOPException if an error occurs while setting up the fonts
*/
public static FontInfo createFontInfo(Configuration cfg, boolean useComplexScriptFeatures)
throws FOPException {
FontInfo fontInfo = new FontInfo();
final boolean strict = false;
if (cfg != null) {
URI thisUri = new File(".").getAbsoluteFile().toURI();
InternalResourceResolver resourceResolver
= ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri);
//TODO The following could be optimized by retaining the FontManager somewhere
FontManager fontManager = new FontManager(resourceResolver, FontDetectorFactory.createDefault(),
FontCacheManagerFactory.createDefault());
//TODO Make use of fontBaseURL, font substitution and referencing configuration
//Requires a change to the expected configuration layout
DefaultFontConfig.DefaultFontConfigParser parser
= new DefaultFontConfig.DefaultFontConfigParser();
DefaultFontConfig fontInfoConfig = parser.parse(cfg, strict);
DefaultFontConfigurator fontInfoConfigurator
= new DefaultFontConfigurator(fontManager, null, strict);
List<EmbedFontInfo> fontInfoList = fontInfoConfigurator.configure(fontInfoConfig);
fontManager.saveCache();
FontSetup.setup(fontInfo, fontInfoList, resourceResolver, useComplexScriptFeatures);
} else {
FontSetup.setup(fontInfo, useComplexScriptFeatures);
}
return fontInfo;
}
您可以通过两种方式解决此问题:
- 如 Bob Schultz 所述,在 Apache FOP 中禁用自动扫描字体。如果这样做,您将必须手动配置 Apache FOP 的字体。
- 不要像 snyman 提到的那样在文件系统根目录中启动 Java 应用程序。在这种情况下,您可以继续使用自动扫描字体。
禁用自动扫描
这是使用配置文件配置 Apache FOP 的代码片段。如果您不在该文件中启用自动扫描,则不必以编程方式禁用它。
// Load configuration for manually configuring fonts
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.build(ResourceUtil.getResourceStream("path/to/config"));
PDFTranscoder transcoder = new PDFTranscoder();
transcoder.configure(cfg);
// Disable auto scanning for fonts programatically - not necessary if you
// don't enable auto scan in your config file
// transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);
在单独的文件夹中启动应用程序
通过指定 WORKDIR
一切都发生在这个文件夹中。自动扫描在那里运行并快速顺利完成。
FROM openjdk:8-jre-alpine
WORKDIR /app
ARG JAR_FILE=target/myapp-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
...
ENTRYPOINT ["java","-jar","app.jar"]
我们在 docker 容器中 运行 安装一个 Spring 应用程序。我们的应用程序可以获取 SVG 文件并将它们运行转换为 PDF 格式以嵌入到 PDF 中。
该应用程序按预期在 osx 和 t运行 代码上正常工作。然而,当 运行 来自具有不同文件系统的 docker 容器时,t运行scoder 会卡住并在一些奇怪的递归文件搜索循环中击败 cpu。
java.lang.Thread.State: RUNNABLE
at java.io.UnixFileSystem.getBooleanAttributes0(Native Method)
at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242)
at java.io.File.isFile(File.java:882)
at org.apache.commons.io.filefilter.FileFileFilter.accept(FileFileFilter.java:59)
at org.apache.commons.io.filefilter.AndFileFilter.accept(AndFileFilter.java:122)
at org.apache.commons.io.filefilter.AndFileFilter.accept(AndFileFilter.java:122)
at org.apache.commons.io.filefilter.OrFileFilter.accept(OrFileFilter.java:118)
at java.io.File.listFiles(File.java:1291)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:357)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364)
at org.apache.commons.io.DirectoryWalker.walk(DirectoryWalker.java:364
下面是 运行 PDFT运行scoder 线程的堆栈跟踪。 Walk 被递归调用了一段时间,然后最终调用了 getBooleanAttributes0 并且一切都阻塞了。
经过进一步研究,我们发现我们可以仔细查看 strace
命令发生的情况,发现系统本质上是在无限循环中发送以下垃圾邮件。
stat("/./sys/devices/pci0000:00/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/PNP0103:00/subsystem/devices/pcspkr/input/input1/subsystem/input0/subsystem/input0/uniq", {st_mode=S_IFREG|0444, st_size=4096, ...}) = 0 <0.000224>
我们似乎在统计调用中被阻塞或挂起。但是我们现在已经对系统调用进行了如此深入的研究,以至于很难调试。有人有什么想法吗?
我的项目遇到了同样的问题。我通过将 batik 降级到 1.7 版本来解决它。
我希望这对你有用。
尝试将参数“-Duser.dir=/%CATALINA_HOME/”添加到您的 CATALINA_OPTS。我在我的centos服务器上遇到了同样的问题。
我遇到了同样的问题。 通过在 DockerFile 中设置 WORKDIR 变量解决了这个问题。 我将它设置为我的部署目录,我在其中复制 spring jar 文件。即:
WORKDIR ${DEPLOYMENT_DIR}
在 pom 中使用最新的蜡染库
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-all</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>fop</artifactId>
<version>2.2</version>
</dependency>
我遇到了同样的错误。在尝试了很多方法来修复它之后,我得出的结论是您在 Mac OS X 上可以使用字体,而您的(无头)Docker 容器 OS没有字体。在到处搜索字体时,转码器并没有优雅地失败。我通过强制代码转换器 use the default fonts (并且不自动查找其他字体)解决了这个问题,如下所示:
...
PDFTranscoder transcoder = new PDFTranscoder();
transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);
...
transcoder.transcode(transcoderInput, transcoderOutput);
...
请注意,当它遇到 14 种字体之外的一种时,当然会退回到其已知字体的缺点。我试过一些方法来解决这个问题,但到目前为止没有成功。
我希望这对某人有所帮助。
我遇到了同样的问题并在我的案例中解决了。这个线程帮助了很多。现在我想将所有部分放在一起 - 也许也适用于遇到此问题的其他人。
这是因为您启动 Java 应用程序的目录。我认识到在以下情况下会出现此问题:
- Java 应用程序在文件系统根目录中启动。
- Apache FOP 中启用了自动扫描字体。
我在
Apache FOP 使用您的 Java 应用程序的工作目录来扫描字体。在这种情况下,这是文件系统根目录。因此将扫描整个文件系统。
以下代码复制自PDFDocumentGraphics2DConfigurator
。它表明使用了 new File(".").getAbsoluteFile().toURI()
- 这是工作目录 resp。 Java 应用程序启动的目录。
/**
* Creates the {@link FontInfo} instance for the given configuration.
* @param cfg the configuration
* @param useComplexScriptFeatures true if complex script features enabled
* @return the font collection
* @throws FOPException if an error occurs while setting up the fonts
*/
public static FontInfo createFontInfo(Configuration cfg, boolean useComplexScriptFeatures)
throws FOPException {
FontInfo fontInfo = new FontInfo();
final boolean strict = false;
if (cfg != null) {
URI thisUri = new File(".").getAbsoluteFile().toURI();
InternalResourceResolver resourceResolver
= ResourceResolverFactory.createDefaultInternalResourceResolver(thisUri);
//TODO The following could be optimized by retaining the FontManager somewhere
FontManager fontManager = new FontManager(resourceResolver, FontDetectorFactory.createDefault(),
FontCacheManagerFactory.createDefault());
//TODO Make use of fontBaseURL, font substitution and referencing configuration
//Requires a change to the expected configuration layout
DefaultFontConfig.DefaultFontConfigParser parser
= new DefaultFontConfig.DefaultFontConfigParser();
DefaultFontConfig fontInfoConfig = parser.parse(cfg, strict);
DefaultFontConfigurator fontInfoConfigurator
= new DefaultFontConfigurator(fontManager, null, strict);
List<EmbedFontInfo> fontInfoList = fontInfoConfigurator.configure(fontInfoConfig);
fontManager.saveCache();
FontSetup.setup(fontInfo, fontInfoList, resourceResolver, useComplexScriptFeatures);
} else {
FontSetup.setup(fontInfo, useComplexScriptFeatures);
}
return fontInfo;
}
您可以通过两种方式解决此问题:
- 如 Bob Schultz 所述,在 Apache FOP 中禁用自动扫描字体。如果这样做,您将必须手动配置 Apache FOP 的字体。
- 不要像 snyman 提到的那样在文件系统根目录中启动 Java 应用程序。在这种情况下,您可以继续使用自动扫描字体。
禁用自动扫描
这是使用配置文件配置 Apache FOP 的代码片段。如果您不在该文件中启用自动扫描,则不必以编程方式禁用它。
// Load configuration for manually configuring fonts
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.build(ResourceUtil.getResourceStream("path/to/config"));
PDFTranscoder transcoder = new PDFTranscoder();
transcoder.configure(cfg);
// Disable auto scanning for fonts programatically - not necessary if you
// don't enable auto scan in your config file
// transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);
在单独的文件夹中启动应用程序
通过指定 WORKDIR
一切都发生在这个文件夹中。自动扫描在那里运行并快速顺利完成。
FROM openjdk:8-jre-alpine
WORKDIR /app
ARG JAR_FILE=target/myapp-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
...
ENTRYPOINT ["java","-jar","app.jar"]