【问题标题】:Apache Batik Transcoder inside Docker Container BlockingDocker 容器阻塞中的 Apache Batik 转码器
【发布时间】:2019-06-15 22:45:51
【问题描述】:

我们在 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

以下是运行 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>

我们似乎在统计调用中被阻止或挂起。但是现在我们已经深入研究了系统调用,以至于它很难调试。有人有什么想法吗?

【问题讨论】:

    标签: java docker svg apache-fop batik


    【解决方案1】:

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

    ...
    PDFTranscoder transcoder = new PDFTranscoder();
    transcoder.addTranscodingHint(PDFTranscoder.KEY_AUTO_FONTS, false);
    ...
    transcoder.transcode(transcoderInput, transcoderOutput);
    ...
    

    请注意,这当然有不利的一面,即当遇到 14 种字体之外的一种字体时,它会退回到其已知字体。我尝试了一些方法来解决这个问题,但到目前为止没有运气。

    我希望这对某人有所帮助。

    【讨论】:

    • 太棒了!我们很久以前就找到了这个修复程序,但我一直不明白为什么会出现这个问题。您的解释与我们的问题完全一致,并且完全解释了发生的事情。感谢您的答复。我会将其标记为答案,以供人们将来参考。
    【解决方案2】:

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

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

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

    我在Infinite scan for fonts in Apache FOP on CentOS 找到了类似的帖子。 Fyodor Sherstobitov 的解释听起来很有道理。

    Apache FOP 使用 Java 应用程序的工作目录来扫描字体。在这种情况下,这是文件系统根。因此将扫描整个文件系统。

    以下代码复制自PDFDocumentGraphics2DConfigurator。它表明使用了new File(".").getAbsoluteFile().toURI() - 这是工作目录。启动 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 应用程序。在这种情况下,您可以继续使用自动扫描字体。

    禁用自动扫描

    这是代码的 sn-p,它使用配置文件配置 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"]
    

    【讨论】:

      【解决方案3】:

      我有同样的问题。 通过在 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>
      

      【讨论】:

      • 谢谢!这也解决了我的问题。对我来说,fop 线程只是简单地死了,没有任何日志消息或异常
      【解决方案4】:

      我的项目也遇到了同样的问题。我通过将 batik 降级到 1.7 版本来解决它。

      我希望这对你有用。

      【讨论】:

        【解决方案5】:

        尝试将参数“-Duser.dir=/%CATALINA_HOME/”添加到您的 CATALINA_OPTS。我在我的 centos 服务器上遇到了同样的问题。

        【讨论】:

          猜你喜欢
          • 2018-04-05
          • 2016-12-26
          • 2015-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多