在服务器请求之后,测量JSF视图的渲染时间

10

我想要测量JSF应用程序的渲染时间。由于某些原因,我无法在应用程序中生成日志。

因此,我的问题是,有没有办法在使用任何浏览器时,在执行包括后端(服务器)调用的特定操作后测量应用程序的渲染时间?

迄今为止,在使用Chrome开发者工具后,我注意到以下内容。在“网络”选项卡上,每个请求都显示了“时间”。此外,在选择某个条目后,在“时间轴”选项卡上,会显示更详细的可视化效果。现在,我可以从中看出,“等待”的往返服务器的时间已经被捕获,但实际的渲染时间如何呢?

假设整个请求花费1秒钟,而“等待”部分需要500毫秒,那么我可以推断出渲染时间是1秒钟减去500毫秒吗?我认为不行,这就是我问这个问题的原因。

长话短说,我需要知道从浏览器中,对于一个特定的请求,服务器处理和实际UI渲染分别花费了多长时间。

如果您有任何提示,将不胜感激。谢谢。

1个回答

11
你可以使用自定义的 ViewDeclarationLanguage 来实现这一点,该方法测量了 createView()buildView()renderView() 以及必要时的 restoreView() 方法。

以下是一个快速入门示例:

public class VdlLogger extends ViewDeclarationLanguageWrapper {

    private static final Logger logger = Logger.getLogger(VdlLoggerFactory.class.getName());

    private ViewDeclarationLanguage wrapped;

    public VdlLogger(ViewDeclarationLanguage wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public UIViewRoot createView(FacesContext context, String viewId) {
        long start = System.nanoTime();
        UIViewRoot view = super.createView(context, viewId);
        long end = System.nanoTime();
        logger.info(String.format("create %s: %.6fms", viewId, (end - start) / 1e6));
        return view;
    }

    @Override
    public void buildView(FacesContext context, UIViewRoot view) throws IOException {
        long start = System.nanoTime();
        super.buildView(context, view);
        long end = System.nanoTime();
        logger.info(String.format("build %s: %.6fms", view.getViewId(), (end - start) / 1e6));
    }

    @Override
    public void renderView(FacesContext context, UIViewRoot view) throws IOException {
        long start = System.nanoTime();
        super.renderView(context, view);
        long end = System.nanoTime();
        logger.info(String.format("render %s: %.6fms", view.getViewId(), (end - start) / 1e6));
    }

    @Override
    public ViewDeclarationLanguage getWrapped() {
        return wrapped;
    }

}

为了运行它,请创建以下工厂:

public class VdlLoggerFactory extends ViewDeclarationLanguageFactory {

    private ViewDeclarationLanguageFactory wrapped;

    public VdlLoggerFactory(ViewDeclarationLanguageFactory wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ViewDeclarationLanguage getViewDeclarationLanguage(String viewId) {
        return new VdlLogger(wrapped.getViewDeclarationLanguage(viewId));
    }

    @Override
    public ViewDeclarationLanguageFactory getWrapped() {
        return wrapped;
    }

}

请按以下方式在 faces-config.xml 中进行注册:

<factory>
    <view-declaration-language-factory>com.example.VdlLoggerFactory</view-declaration-language-factory>
</factory>

createView()是根据视图文件中的<f:view><f:metadata>创建具体的UIViewRoot实例的步骤。在使用Facelets(XHTML)作为视图时,此步骤将通过SAX解析器解析所有关联的XHTML文件,并缓存一段时间,该时间由javax.faces.FACELETS_REFRESH_PERIOD定义。因此,可能会有一次相对较慢,另一次快速。

buildView()是基于视图(XHTML)组成填充JSF组件树(UIViewRootgetChildren())的步骤。在此步骤中,将执行所有标记处理程序(JSTL和朋友们),并计算那些标记处理程序和组件的idbinding属性中的所有EL表达式(详见JSTL in JSF2 Facelets... makes sense?)。因此,如果在视图构建期间首次构造备份bean,并在@PostConstruct期间调用业务逻辑,则可能会发生这种耗时情况。

renderView()是基于JSF组件树和模型生成HTML输出的步骤,从UIViewRoot#encodeAll()开始。因此,如果在视图渲染期间首次构造备份bean,并在@PostConstruct期间调用业务逻辑,则可能会发生这种耗时情况。

如果备份bean在getter方法中而不是在@PostConstruct或任何其他一次性生命周期事件监听器中不正确地执行业务逻辑,则可能会发生更多时间的消耗。详见Why JSF calls getters multiple times


太棒了。谢谢! - user2271933
ViewDeclarationLanguageWrapper是在2.2中添加的。您认为修改它以使其与2.1一起使用会容易些,还是最好寻找另一种实现方式? - Sam Hasler
1
@SamHasler:不要自己(通过IDE)生成ViewDeclarationLanguageWrapper,而是应该扩展ViewDeclarationLanguage源代码并不高深莫测 ;) - BalusC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接