【问题标题】:Generating big files with Struts2 + Freemarker使用 Struts2 + Freemarker 生成大文件
【发布时间】:2017-07-19 04:40:24
【问题描述】:

我需要使用Struts2Freemarker 将数据转储到一个大的xml(50 ~ 500 Mb)中:

<?xml version="1.0" encoding="UTF-8"?>
<documents>
    <#list collection as document>
        <document>
        ...
        </document>
        <#if document_index % 100 == 0> 
           <#flush/>
        </#if>
    </#list>
</document>

由于collection 可能很大,它已经实现了一个内部逻辑来获取部分数据,只要有可用数据,就可以使用它并再次获取。

问题似乎与 XML 的缓冲区有关:有时应用程序崩溃(java.lang.OutOfMemoryError)。

每 100 个文档调用一次 Freemarker flush,但我不知道是否真的发生了刷新,根据 documentation,这只是嵌入软件的指示 (Strut2 + Tomcat7)。

关于如何强制刷新有什么建议吗?

JVM 处理了大约 1~2 Gb 的堆内存,也许只是内存问题?

也许org.apache.struts2.views.freemarker.FreemarkerManager 可以为此目的适当调整?

【问题讨论】:

    标签: java tomcat struts2 freemarker flush


    【解决方案1】:

    在这个用例中不需要调用#flush。当Writer(或它后面的OutputStream)被缓冲以提高性能时,缓冲区无论如何都有一个相当低的大小限制(几十千字节等),之后它将自动刷新。如果输出由于某种功能原因被缓冲,例如,为了确保输出可以在任何时候完全回滚,或者输出可以作为单个 String 返回,那么无论如何你都应该修复(这就是#flush 也可能被忽略的地方)。因此,尝试在freemarker.template.Template.createProcessingEnvironment 上设置一个断点并检查调试器中的out Writer 参数(以及Writer/OutputStream 表示Writer 写入等)。也许某处会有StringWriter

    【讨论】:

    • 深入研究 Struts 代码我发现了问题,我的 FreemarkerManager 正在使用 TemplateExceptionHandler.RETHROW_HANDLER 并且在 Struts 实现中 (FreemarkerResult) 这会导致编写器仅在 结束时被刷新 的模板处理。 @ddekany:你知道是否有办法为单个请求更改TemplateExceptionHandler
    • 如果使用RETHROW_HANDLER,延迟刷新的逻辑在哪里?在 Struts 中?为什么需要显式冲洗(见我上面的回答)?否则,是的,您可以调用 Environment.setTemplateExceptionHandler 将其更改为单个模板处理,但我不确定您在 Struts 中的哪个位置有机会调用它。
    • 好的,我查看了 Struts 源代码...确实,使用 RETHROW_HANDLER 它不会将实际的 Writer 传递给 FreeMarker,而是将所有内容收集到 CharArrayWriter 中,所以冲洗不会有任何作用。 Environment.setTemplateExceptionHandler 对你没用,因为那里的逻辑只查看全局 Configuration。而且我看不出你有什么可以勾搭的地方……
    • 顺便说一句,使特定模板使用与其他模板不同的配置设置的官方方法是通过 template_configurations 配置设置(自 2.3.24 起)。有了它,您可以在 FreeMarker 配置中声明 huge-output.ftl 使用另一个 TempalteExceptionHandler。不幸的是,由于 Struts(错误地)调用 Configuration.getTemplateExceptionHandler 而不是 Template.getTemplateExceptionHandler ,这也无济于事......
    • 正确,正如我在回答中提到的那样,Struts FreemarkerResult 有点僵化,不会让您以我希望的方式从缓冲行为切换到刷新行为。我在这里为 Struts 提出了一个补丁:issues.apache.org/jira/browse/WW-4749
    【解决方案2】:

    我找到了解决办法。

    问题在于 Struts2 FreemarkerResult 以及这个类如何让您处理输出写入器的刷新。

    基本上,如果您使用TemplateExceptionHandler.RETHROW_HANDLER 作为异常处理程序(就像我一样),则整个模板都会在执行任何刷新之前被处理。 (这是所需的行为,因为如果有任何模板错误,您希望在将任何输出发送到响应之前在更高级别引发和处理异常)

    TemplateExceptionHandler 是嵌入在FreemarkerManager 中的固定配置,不能根据给定请求的需要进行切换。

    我决定扩展 FreemarkerResult 重写方法 doExecute(...) 以包含一个参数,即使全局配置为 @987654329,该参数也会强制单个请求使用标准 flushing-during-template-process @。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-05
    • 2021-11-28
    • 2011-12-03
    • 2012-06-25
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 2018-05-16
    相关资源
    最近更新 更多