【问题标题】:JSF Performance improvementJSF 性能改进
【发布时间】:2015-03-21 16:23:58
【问题描述】:

我正在使用 JSF 2.2 + Primefaces 开发一个 WebApp...项目正在快速增长,第一次性能测试非常糟糕(由于我对 JSF 生命周期以及非功能性需求的了解不足 - 即完全 ajax网站),那么我们可以改进一点,但结果仍然不如预期。 (根据动作,我们有 300~1500 毫秒的时间,想法是在 500 毫秒左右进行一次表演,给予或接受)。主要是 Restore View 阶段和 Render Response 是时间消费者(在其他阶段,花费的时间毫无价值)。对于某些操作,Invoke Application 也需要时间(由于 DB 调用)。

在阅读了网络上的大量文章后,有很多很棒的技巧需要考虑(当然很多来自 StackOverflow),例如:

  • 改进数据库查询

我们有一些复杂的查询是通过两个 Hibernate Criteria 查询完成的,所以我们必须在这里工作。 (也许使用纯 SQL 查询,复杂的使用子查询?)

  • 永远不要在 Bean 的 getter 上定义业务逻辑

知道了!

  • 将适当的范围设置为 bean 并在其上存储必要的东西

我们有一个完整的 ajax 站点,所以 View Scoped 对我们来说几乎是 Session Scoped,我们使用 SessionBeans 作为一种“缓存”来存储我们不希望每次都从 DB 获取的关键数据 +在这里定义业务逻辑。

  • 选择正确的 JSF 状态保存方法 - Client Vs Server

为此,我必须进行更多研究,检查各种可能性及其优缺点,然后测试每种可能性。

到目前为止非常清楚,现在一些我有疑问的额外提示。

  • 尽可能使用原版 HTML,最好使用 h: 标签而不是 p: 标签

纯 HTML 清晰且有意义,现在介于 h: 和 p: 之间,它值多少钱?例如。

<p:commandButton value="Value" 
             styleClass="class" 
             actionListener="#{myBean.doStuff()}"
             ajax="true" process="@form" update="@form" 
             onsuccess="jsFunction()" />

<h:commandButton value="Value" 
             styleClass="class" 
             actionListener="#{myBean.doStuff()}" >
       <f:ajax execute="@form" render="@form" event="onclick" />
</h:commandButton>

<ui:fragment... vs <p:fragment...

<p:outputLabel value="#{myBean.value}" rendered="#{myBean.shouldRender}" />

<ui:fragment rendered="#{myBean.shouldRender}">
    <label>#{myBean.value}</label>
</ui:fragment>

我一直在混合使用 Primefaces 和 Jsf 标签和一些纯 HTML 有一段时间了。(主要是 PF,因为它们的组件特性)我现在纯 HTML 总是更快,但在 JSF 和另一个框架?如果我这样做,更改它会花费大量时间,而且我不喜欢知道它根本不会产生相关影响的结果。

  • 自定义 Facelets 标签与复合组件

我认为这是关键。仍然对它们的差异有一些疑问,在两者的实现上,CC 使用起来非常简单和灵活,但缺点是它们完全包含在 ViewTree 中,并且 JSF 会为每个请求重新生成(如果我没记错的话),虽然 custom tags 似乎使用起来有点复杂(没那么多),但它的优点是只有实际呈现的内容才包含在 ViewTree 中,仅此而已,减少 RESTORE VIEW 的耗时。我们有几个复合组件,没有 Facelets 标签,所以这里有很多工作要做。我仍然没有找到一篇很好的文章来解释它们之间的差异,什么时候应该使用一个,什么时候使用另一个(已经阅读了输入,消息使用 TAGS 以及更复杂的事情 CC)。如果这个想法是更喜欢标签而不是 CC,在这种情况下我别无选择,而不是使用 CC?是否可以在 CC 中使用自定义标签,让 JSF 更轻松地处理它们?

我即将进入修改洞项目的旅程以获得更好的性能,这将需要我几天的时间,我的想法是这次获得更好的结果;所以每一个提示,建议和建议都非常受欢迎!谢谢你们的时间!

【问题讨论】:

  • 我将首先测量 300~1500 毫秒的花费,以便您知道需要改进的地方。首先,您可以使用 PhaseListener 来衡量每个阶段花费了多少,这将给出一个模糊的概念,即页面结构或对数据库的访问是否对延迟负责,具体取决于哪个阶段成本最高。此外,如果您有 DAO 层,则可以使用某些方面将调用记录到该层以衡量数据库成本。
  • @Diego 对不起 diego,我忘了在我的问题上添加这个。当然,我一直在用探查器进行测量。渲染响应和恢复视图占用了 98% 的时间
  • 如果您有很多输入或复杂的输入,请尝试在 ajax 调用上使用PF partial submit,节省大量时间,还可以尝试使用process attribute 可以做什么...
  • 还要检查你是否真的需要在“doStuff()”之后更新完整的表单。如果组件知道要在 ajax 调用中更新哪些其他组件,您很可能根本不需要更新表单,除非您必须完全替换页面
  • 这对于 Stack Overflow 来说实在是太宽泛了。它读起来几乎就像一个博客,而不是一个问题。描述并确定性能最佳的消费者,如果您不知道如何改进它,请具体询问有关它的问题。冲洗并重复。

标签: performance jsf jsf-2 primefaces


【解决方案1】:

您可以采取一些措施来提高屏幕的性能

  1. GZIP 过滤器将显着减少初始加载时间。它在传输到客户端浏览器时压缩页面内容。参考https://stackoverflow.com/a/35567295/5076414
  2. 您还可以实现一个 cacheFilter 来带来 您的屏幕与基于 JavaScript 的 UI 相当。这将缓存屏幕的静态内容,例如图标、图像、样式表、javascript 等。您可以控制要缓存的内容和要排除的内容。参考https://stackoverflow.com/a/35567540/5076414
  3. 对于客户端 UI 组件,您可以使用 Primefaces,即 JQuery 基于用户界面。

如何验证我的屏幕是否使用 gzip 和缓存

要查看您的内容是否已经使用 gzip 和缓存,请在您的 Google Chrome 浏览器中 -> 右键单击​​屏幕 -> 检查 -> 单击网络选项卡 -> 刷新屏幕。 点击图片、图标、样式表,看看您是否在响应标题

中看到以下内容

Cache-Control:max-age=2592000 如果元素的状态为 304(来自缓存)

Content-Encoding:gzip 如果元素的状态为 200

【讨论】:

    【解决方案2】:

    RenderResponse 占用了您 98% 的时间,因为……这就是 JSF 的主要工作。如果 JSF 将 50% 的请求处理时间用于渲染响应之外的事情,我会担心!你需要深入挖掘。考虑这个例子。

    假设您有一个显示表格的页面。您使用 p:dataTable PrimeFaces 组件,如下所示:

    <h:html>
       <h:body>
           <p:dataTable value="#{myBean.data}">
           </p:dataTable>
       </h:body>
    </h:html>
    

    你有这个@ManagedBean

    @ManagedBean(name="myBean")
    public class MyBean {
    
        public List<MyObject> getData() { 
            return // fetch list from database;
        }
    }
    

    每次调用getData() 时,都会访问数据库,并且该时间会添加到渲染阶段的全局时间。此外,p:dataTable 的呈现可能需要多次调用 getData。

    同样,您需要更深入地研究您的应用程序,并检测在渲染阶段究竟在哪里花费了时间。根据我的经验,@ManagedBeans 的幼稚实现在 xhtmls 中引用的相同 getter 中有很多数据库查询代码,就像我之前的示例一样。 JSF(或像 PrimeFaces 的 p:dataTable 这样的组件)可能会在渲染单个 xhtml 时多次调用这些 getter。一个常见的策略是只获取一次数据,使用 preRenderView 事件,如下所示:

    在你的 xhtml 中:

    <h:html>
       <f:metadata>
           <f:event type="preRenderView" listener="#{myBean.fetchDAta}"/>
       </f:metadata>
       <h:body>
           <p:dataTable value="#{myBean.data}">
           </p:dataTable>
       </h:body>
    </h:html>
    

    在您的托管 bean 中:

    @ManagedBean(name="myBean")
    public class MyBean {
        private List<MyObject> data;
    
        public void fetchData() {
             data = // fetch data from database.
        }
    
        public List<MyObject> getData() { 
            return data; 
        }
     }
    

    【讨论】:

    • 或者在创建bean时使用@PostConstruct来取数据。
    【解决方案3】:

    半年前我也遇到过同样的问题。大部分时间花在 RestoreViewRenderResponse 阶段。如果你的问题是一样的那么 你无能为力:

    1. 尽可能使用partialSubmit="true"
    2. 使用execute(或PrimeFaces process)属性来减少服务器端的处理
    3. 减少组件数量
    4. 不要大量使用复合组件

    在我的特殊情况下,我有很多没有输入控件的动态 html 和 我使用复合组件作为条件渲染的模板。 为了提高性能,我使用了freemarker 来生成动态 html。

    PS。如果我们有包含许多组件的复杂页面,我的意见是不要使用 JSF。我认为Tapestry 对此进行了更优化。它不像 JSF 那样在每个请求上重新创建组件。

    【讨论】:

      【解决方案4】:

      我不确定“p”和“h”之间是否有很大区别,因为它们最后都呈现为 html。我看到你使用了很多 process 属性。您可以通过仅发送部分表单来定位此属性中的特定 ID 以提高性能。另一个提高性能的想法是使用 primefaces “缓存”。 http://www.primefaces.org/showcase/ui/misc/cache.xhtml 用于您希望非常频繁地呈现的非动态内容(查看文档)。我猜“缓存”会尝试从缓存中获取树视图而不是重建它。

      【讨论】:

      • “p”和“h”是有区别的。 “p”使用 javascript 等渲染更复杂的 html,尤其是带有大量记录的 p:selectOneMenu,例如“复杂”模式,它比h:selectOneMenu 慢得多。那不好吗?不,这是一种权衡……
      【解决方案5】:

      如果你想用其他框架制作 jsf 应用程序,你的应用程序对用户来说性能很差。因为 Jsf 应用程序变成了等待渲染阶段的 6 阶段太多。因此,您可以将微服务用于后端,将 javascript 用于前端。结果你可以做出像性能和架构一样好的应用程序。您可以查看 java ee 应用程序的 spring boot 微服务

      【讨论】:

        【解决方案6】:

        我阅读了一篇关于 JSF 加速性能的有趣文章。在恢复视图阶段,JSF 重构组件树,包括将数据绑定到数据表,从而导致从数据库中不必要地获取数据。本文着眼于一种避免额外工作来获取数据的方法。

        我在这里报告可能的解决方案,以及完整的文章以供参考。

        解决该问题的方法是完全跳过第一次获取数据。它不需要,甚至没有使用,甚至不是正确的数据(别担心,我稍后会解释)。在 FacesContext 对象中有一个名为 getRenderResponse() 的方法,如果调用了渲染响应方法并且我们处于渲染阶段,则该方法返回 true。在我们渲染页面之前,我们并不真正需要我们的数据,所以我们只在此方法返回 true 时才加载它。

        例如:

        //generates the list of data, think of this as an expensive DB operation
        private List<Integer> generateData()  {
        if (!FacesContext.getCurrentInstance().getRenderResponse()) {
            return null;
        }
        System.out.println("Generating Data starting from "+pageNumber);
        ...
        ...
        ...
        }
        

        因此,即使在回发时,我们每个页面请求也只会获取一次数据,因为在渲染响应中真正需要数据之前,我们不会费心加载数据,如果您查看时间,我们的页面加载速度是自我们只获取了一半的数据。

        所以你去吧,你可以通过只在你真正需要的时候加载数据来提高你的 JSF 页面的速度,我猜是一种懒惰的延迟加载数据。

        Double the speed of (some of) your JSF pages (and dodge bugs too)

        希望对其他人有用。

        【讨论】:

          猜你喜欢
          • 2019-12-25
          • 2016-12-30
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-12-17
          • 2010-11-01
          相关资源
          最近更新 更多