Docker 容器阻塞中的 Apache Batik 转码器

Lia*_*iam 4 java svg batik apache-fop docker

我们在 docker 容器中运行 Spring 应用程序。我们的应用程序可以获取 SVG 文件并将它们转换为 PDF 格式以嵌入到 PDF 中。

该应用程序在 osx 上正常工作并按预期进行转码。然而,当从具有不同文件系统的 docker 容器内部运行时,转码器卡住并在一些奇怪的递归文件搜索循环中颠簸 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
Run Code Online (Sandbox Code Playgroud)

下面是运行 PDFTranscoder 的线程的堆栈跟踪。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>

我们似乎在 stat 调用中被阻止或挂起。但是我们现在已经深入研究了系统调用,结果证明它很难调试。有没有人有任何想法?

Bob*_*ltz 5

我遇到了同样的错误。在尝试了很多方法来修复它之后,我得出的结论是,您在 Mac OS X 上可以使用字体是一个问题,而您的(无头)Docker 容器操作系统没有字体。在到处搜索字体时,转码器并没有优雅地失败。我通过强制转码器使用默认字体(并且不自动查找其他字体)来解决它,如下所示:

...
PDFTranscoder transcoder = new PDFTranscoder();
transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);
...
transcoder.transcode(transcoderInput, transcoderOutput);
...
Run Code Online (Sandbox Code Playgroud)

请注意,这当然有缺点,当它遇到 14 种字体之外的一种时,会退回到其已知字体。我试图解决这个问题,但到目前为止还没有运气。

我希望这可以帮助别人。


小智 5

我遇到了同样的问题并在我的情况下解决了它。这个线程有很大帮​​助。现在我想把所有的部分放在一起——也许也适用于遇到这个问题的其他人。

这样做的原因是您启动 Java 应用程序的目录。我认识到在以下情况下会出现此问题:

  • Java 应用程序在文件系统根目录中启动。
  • Apache FOP 中启用了自动扫描字体。

我在Infinite scan for fonts in Apache FOP on CentOS 中找到了类似的帖子。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;
    }
Run Code Online (Sandbox Code Playgroud)

您可以通过两种方式解决此问题:

  • 禁用自动扫描 Apache FOP 中的字体,如 Bob Schultz 所述。如果这样做,则必须手动配置 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);
Run Code Online (Sandbox Code Playgroud)

在单独的文件夹中启动应用程序

通过指定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"]
Run Code Online (Sandbox Code Playgroud)