关于页面加载时间测量的想法

时间:2013-05-16 22:45:44

标签: jsf jsf-2

问题是 - 您如何衡量页面加载的时间,您使用的技术,您可以提供哪些推荐,以及您拥有哪些积极和消极的体验。

麻烦的是,即使是jsf中的轻页也可能需要10秒才能加载。这些页面不需要任何评估,渲染资源等。显而易见的答案 - 它在渲染队列中......好吧,但可能是其他东西?

似乎需要从请求开始测量时间,包括渲染,评估,数据传输时间,客户端渲染时间等。

您是否有任何良好的工作方式,工具链,工具可以在JSF页面中查看完整的页面时间?

使用Glassfish-3,JSF-2。机器有64个CPU。

2 个答案:

答案 0 :(得分:6)

这是我刚刚制作和测试的一个简单的自定义解决方案。它提供了一些执行和渲染时间的统计信息。当然,所有信息都只来自服务器端处理。

结果示例:

Date                     ID request  URL                                  Phase              Execution time
2013-05-16 21:10:29.781  34          http://localhost:8080/web/page.jspx  RESTORE_VIEW 1     15
2013-05-16 21:10:29.796  34          http://localhost:8080/web/page.jspx  RENDER_RESPONSE 6  4438
2013-05-16 21:10:39.437  35          http://localhost:8080/web/page.jspx  RESTORE_VIEW 1     16
2013-05-16 21:10:39.453  35          http://localhost:8080/web/page.jspx  RENDER_RESPONSE 6  3937

实施非常严格。首先,您必须在faces-config.xml

中配置 PhaseListener
<lifecycle>
    <phase-listener>com.spectotechnologies.jsf.phaselisteners.PhaseProcessesAnalyserListener</phase-listener>
</lifecycle>

这是帮助程序类,用于保存每个处理的信息:

package com.spectotechnologies.website.common.helper;

import java.util.Date;
import javax.faces.event.PhaseId;

/**
 *
 * @author Alexandre Lavoie
 */
public class PhaseProcess
{
    private int m_nIDRequest;
    private String m_sURL;
    private Date m_oStart;
    private Date m_oEnd;
    private PhaseId m_oPhase;

    public void setIdRequest(int p_nIDRequest)
    {
        m_nIDRequest = p_nIDRequest;
    }

    public int getIdRequest()
    {
        return m_nIDRequest;
    }

    public void setUrl(String p_sURL)
    {
        m_sURL = p_sURL;
    }

    public String getUrl()
    {
        return m_sURL;
    }

    public void setStart(Date p_oStart)
    {
        m_oStart = p_oStart;
    }

    public Date getStart()
    {
        return m_oStart;
    }

    public void setEnd(Date p_oEnd)
    {
        m_oEnd = p_oEnd;
    }

    public Date getEnd()
    {
        return m_oEnd;
    }

    public void setPhase(PhaseId p_oPhase)
    {
        m_oPhase = p_oPhase;
    }

    public PhaseId getPhase()
    {
        return m_oPhase;
    }

    public long getExecutionTime()
    {
        long lExecutionTime = -1;

        if(getEnd() != null)
        {
            lExecutionTime = getEnd().getTime() - getStart().getTime();
        }

        return lExecutionTime;
    }
}

以下是具有业务逻辑的类,请注意,目前统计数据只会增长并且永远不会被删除,因此可能是一个很好的更新,例如只保留最后一小时:

package com.spectotechnologies.website.common.helper;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;

/**
 *
 * @author Alexandre Lavoie
 */
public class PhaseProcesses
{
    private List<PhaseProcess> m_lItems = new ArrayList();
    private int m_nNextIDRequest = 0;

    public static PhaseProcesses getInstance()
    {
        FacesContext oFaces = FacesContext.getCurrentInstance();

        PhaseProcesses oPhaseProcesses = (PhaseProcesses)oFaces.getExternalContext().getSessionMap().get("sessionPhaseProcesses");

        if(oPhaseProcesses == null)
        {
            oPhaseProcesses = new PhaseProcesses();

            FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("sessionPhaseProcesses",oPhaseProcesses);
        }

        return oPhaseProcesses;
    }

    public void set(int p_nIDRequest, String p_sURL, PhaseId p_oPhase, int p_nType)
    {
        PhaseProcess oPhaseProcess;

        // Phase start
        switch(p_nType)
        {
            case 0:
                // start
                oPhaseProcess = new PhaseProcess();

                oPhaseProcess.setIdRequest(p_nIDRequest);
                oPhaseProcess.setUrl(p_sURL);
                oPhaseProcess.setPhase(p_oPhase);
                oPhaseProcess.setStart(new Date());

                if(m_lItems.size() > 250)
            {
                m_lItems.remove(0);
            }

                m_lItems.add(oPhaseProcess);
                break;
            case 1:
                // end
                for(int nPhase = m_lItems.size() - 1;nPhase >= 0;nPhase--)
                {
                    if(m_lItems.get(nPhase).getIdRequest() == p_nIDRequest && m_lItems.get(nPhase).getPhase() == p_oPhase)
                    {
                        m_lItems.get(nPhase).setEnd(new Date());
                        break;
                    }
                }
                break;
        }
    }

    public List<PhaseProcess> getList()
    {
        return m_lItems;
    }

    public Integer getNextIDRequest()
    {
        return m_nNextIDRequest++;
    }
}

以下是着名的 PhaseListener ,其中跟踪了信息:

package com.spectotechnologies.jsf.phaselisteners;

import com.spectotechnologies.website.common.helper.PhaseProcesses;
import java.net.URLEncoder;
import java.util.Enumeration;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpServletRequest;

/**
 *
 * @author Alexandre Lavoie
 */
public class PhaseProcessesAnalyserListener implements PhaseListener
{
    @Override
    public PhaseId getPhaseId()
    {
        return PhaseId.ANY_PHASE;
    }

    @Override
    public void beforePhase(PhaseEvent p_oEvent)
    {
        PhaseProcesses.getInstance().set(getIDRequest(),getURL(),p_oEvent.getPhaseId(),0);
    }

    @Override
    public void afterPhase(PhaseEvent p_oEvent)
    {
        PhaseProcesses.getInstance().set(getIDRequest(),getURL(),p_oEvent.getPhaseId(),1);
    }

    private Integer getIDRequest()
    {
        Integer iIDRequest = (Integer)FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("idrequest");

        if(iIDRequest == null)
        {
            iIDRequest = PhaseProcesses.getInstance().getNextIDRequest();

            FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("idrequest",iIDRequest);
        }

        return iIDRequest;
    }

    private String getURL()
    {
        Enumeration<String> lParameters;
        String sParameter;
        StringBuilder sbURL = new StringBuilder();
        Object oRequest = FacesContext.getCurrentInstance().getExternalContext().getRequest();

        try
        {
            if(oRequest instanceof HttpServletRequest)
            {
                sbURL.append(((HttpServletRequest)oRequest).getRequestURL().toString());

                lParameters = ((HttpServletRequest)oRequest).getParameterNames();

                if(lParameters.hasMoreElements())
                {
                    if(!sbURL.toString().contains("?"))
                    {
                        sbURL.append("?");
                    }
                    else
                    {
                        sbURL.append("&");
                    }
                }

                while(lParameters.hasMoreElements())
                {
                    sParameter = lParameters.nextElement();

                    sbURL.append(sParameter);
                    sbURL.append("=");
                    sbURL.append(URLEncoder.encode(((HttpServletRequest)oRequest).getParameter(sParameter),"UTF-8"));

                    if(lParameters.hasMoreElements())
                    {
                        sbURL.append("&");
                    }
                }
            }
        }
        catch(Exception e)
        {
            // Do nothing
        }

        return sbURL.toString();
    }
}

最后,这是我创建的用于显示统计信息的简单页面。一个很好的改进可能是在页面处理上添加平均值,也就是每个idrequest处理时间。

Bean代码:

package com.spectotechnologies.website.common.beans;

import com.spectotechnologies.website.common.helper.PhaseProcess;
import com.spectotechnologies.website.common.helper.PhaseProcesses;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 *
 * @author Alexandre Lavoie
 */
@ManagedBean
@RequestScoped
public class PagesStatisticsActions
{
    public List<PhaseProcess> getList()
    {
        return PhaseProcesses.getInstance().getList();
    }
}

查看代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core">

    <f:view contentType="application/xhtml+xml">
        <h:head>
            <meta http-equiv="Content-Type" content="application/xhtml+xml;charset=UTF-8" />
        </h:head>

        <h:body>
            <h:dataTable value="#{pagesStatisticsActions.list}" var="item">
                <h:column>
                    <f:facet name="header">
                        Date
                    </f:facet>

                    <h:outputText value="#{item.start}">
                        <f:convertDateTime timeZone="America/Montreal" pattern="yyyy-MM-dd HH:mm:ss.SSS" />
                    </h:outputText>
                </h:column>

                <h:column>
                    <f:facet name="header">
                        ID request
                    </f:facet>

                    #{item.idRequest}
                </h:column>

                <h:column>
                    <f:facet name="header">
                        URL
                    </f:facet>

                    #{item.url}
                </h:column>

                <h:column>
                    <f:facet name="header">
                        Phase
                    </f:facet>

                    #{item.phase}
                </h:column>

                <h:column>
                    <f:facet name="header">
                        Execution time (ms)
                    </f:facet>

                    #{item.executionTime}
                </h:column>
            </h:dataTable>
        </h:body>
    </f:view>
</html>

更新1:

  • 在网址
  • 中添加了参数
  • 添加了250个日志的限制

答案 1 :(得分:1)

以下解决方案基于solution发布的Alexandre Lavoie,但适用于ADF Faces:

<强> PhaseProcessingTimeAnalyserListener

import java.io.Serializable;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Map;
 
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
 
import oracle.adf.controller.v2.lifecycle.Lifecycle;
import oracle.adf.controller.v2.lifecycle.PagePhaseEvent;
import oracle.adf.controller.v2.lifecycle.PagePhaseListener;
import oracle.adf.share.ADFContext;
 
import com.mhis.posm.web.lifecycle.helper.PhaseProcessor;
 
/**
 * Class PhaseProcessingTimeAnalyserListener calculates the execution time of each Phase of ADF
 * @author TapasB
 */
public class PhaseProcessingTimeAnalyserListener implements PagePhaseListener, Serializable {
 
 private static final long serialVersionUID = 6814928970314659328L;
 
 /**
  * Method beforePhase notifies the listener before the execution of a specific phase of the ADF Page Lifecycle.
  * @author TapasB
  * @param phaseEvent
  * @see oracle.adf.controller.v2.lifecycle.PagePhaseListener#beforePhase(oracle.adf.controller.v2.lifecycle.PagePhaseEvent)
  */
 @Override
 public void beforePhase(PagePhaseEvent phaseEvent) {
  int phaseId = phaseEvent.getPhaseId();
  String phaseName = Lifecycle.getPhaseName(phaseId);
  PhaseProcessor.getInstance().process(getRequestId(), getURL(), phaseId, phaseName, PhaseProcessor.PhaseType.BEGIN);
 }
 
 /**
  * Method afterPhase notifies the listener after the execution of a specific phase of the ADF Page Lifecycle.
  * @author TapasB
  * @param phaseEvent
  * @see oracle.adf.controller.v2.lifecycle.PagePhaseListener#afterPhase(oracle.adf.controller.v2.lifecycle.PagePhaseEvent)
  */
 @Override
 public void afterPhase(PagePhaseEvent phaseEvent) {
  int phaseId = phaseEvent.getPhaseId();
  String phaseName = Lifecycle.getPhaseName(phaseId);
  PhaseProcessor.getInstance().process(getRequestId(), getURL(), phaseId, phaseName, PhaseProcessor.PhaseType.END);
 }
 
 /**
  * Method getRequestId generates and returns an unique ID value for each Request
  * @author TapasB
  * @return requestId
  */
 private Integer getRequestId() {
  @SuppressWarnings("unchecked")
  Map<String, Object> requestScope = ADFContext.getCurrent().getRequestScope();
  Integer requestId = (Integer) requestScope.get("requestId");
 
  if (requestId == null) {
   requestId = PhaseProcessor.getInstance().getNextRequestId();
   requestScope.put("requestId", requestId);
  }
 
  return requestId;
 }
 
 /**
  * Method getURL returns the URL in which the application is requested
  * @author TapasB
  * @return a String URL
  */
 private String getURL() {
  Enumeration<String> parameterNames = null;
  String parameterName = null;
  StringBuilder urlBuilder = new StringBuilder();
  Object request = FacesContext.getCurrentInstance().getExternalContext().getRequest();
 
  try {
   if (request instanceof HttpServletRequest) {
    HttpServletRequest servletRequest = (HttpServletRequest) request;
    urlBuilder.append(servletRequest.getRequestURL().toString());
    parameterNames = servletRequest.getParameterNames();
 
    if (parameterNames.hasMoreElements()) {
     if (!urlBuilder.toString().contains("?")) {
      urlBuilder.append("?");
     } else {
      urlBuilder.append("&");
     }
    }
 
    while (parameterNames.hasMoreElements()) {
     parameterName = parameterNames.nextElement();
 
     urlBuilder.append(parameterName);
     urlBuilder.append("=");
     urlBuilder.append(URLEncoder.encode(servletRequest.getParameter(parameterName), "UTF-8"));
 
     if (parameterNames.hasMoreElements()) {
      urlBuilder.append("&");
     }
    }
   }
  } catch (Exception ex) {
  }
 
  return urlBuilder.toString();
 }
}

PhaseProcessor

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
 
import oracle.adf.share.ADFContext;
 
import com.edifixio.osrd.generic.log.Log;
 
/**
 * Class PhaseProcessor processes the Phase execution time
 * @author TapasB
 */
public class PhaseProcessor implements Serializable {
 
 private static final long serialVersionUID = 6658181867505616109L;
 
 private List<Phase> phases = new ArrayList<Phase>();
 private AtomicInteger nextRequestId = new AtomicInteger(1);
 
 /**
  * Constructor PhaseProcessor is private
  * @author TapasB
  */
 private PhaseProcessor() {
 
 }
 
 /**
  * Class PhaseType
  * @author TapasB
  */
 public static enum PhaseType {
  BEGIN,
  END;
 }
 
 /**
  * Method getInstance returns a Session Instance of this class 
  * @author TapasB
  * @return an PhaseProcessor instance
  */
 public static PhaseProcessor getInstance() {
  @SuppressWarnings("unchecked")
  Map<String, Object> sessionScope = ADFContext.getCurrent().getSessionScope();
  PhaseProcessor phaseProcessor = (PhaseProcessor) sessionScope.get("phases");
 
  if (phaseProcessor == null) {
   phaseProcessor = new PhaseProcessor();
   sessionScope.put("phases", phaseProcessor);
  }
 
  return phaseProcessor;
 }
 
 /**
  * Method process processes the {@link Phase} 
  * @author TapasB
  * @param requestId - the unique ID for each request
  * @param url - the url in which the application is requested
  * @param phaseId - ADF's Phase ID
  * @param phaseName - ADF's Phase Name
  * @param phaseType - BEGIN or END
  */
 public void process(int requestId, String url, int phaseId, String phaseName, PhaseType phaseType) {
  Phase phase;
 
  switch (phaseType) {
   case BEGIN:
    phase = new Phase();
 
    phase.setRequestId(requestId);
    phase.setUrl(url);
    phase.setPhaseId(phaseId);
    phase.setPhaseName(phaseName);
    phase.setStartTime(new Date());
 
    phases.add(phase);
 
    Log.info(this, "The Phase: " + phase.getPhaseName(phaseId) + " begins. Requested URL: " + phase.getUrl());
    break;
 
   case END:
    ListIterator<Phase> phaseIterator = phases.listIterator(phases.size());
 
    while (phaseIterator.hasPrevious()) {
     phase = phaseIterator.previous();
 
     if (phase.getRequestId() == requestId && phase.getPhaseId() == phaseId && phase.getPhaseName().equals(phaseName)) {
      phase.setEndTime(new Date());
      Log.info(this, "The Phase: " + phase.getPhaseName(phaseId) + " ends with execution time: '" + (phase.getEndTime().getTime() - phase.getStartTime().getTime()) + "' millisecondes. Requested URL: " + phase.getUrl());
      phaseIterator.remove();
      break;
     }
 
    }
 
    break;
  }
 }
 
 /**
  * Method getNextRequestId returns and increment the unique ID 
  * @author TapasB
  * @return the ID
  */
 public Integer getNextRequestId() {
  return nextRequestId.getAndIncrement();
 }
}

<强>阶段

import java.io.Serializable;
import java.util.Date;
 
import oracle.adf.controller.faces.lifecycle.JSFLifecycle;
 
/**
 * Class Phase represent a phase
 * @author TapasB
 */
public class Phase implements Serializable {
 
 private static final long serialVersionUID = -461595462579265128L;
 
 private int requestId;
 private String url;
 private Date startTime;
 private Date endTime;
 private int phaseId;
 private String phaseName;
 
 /**
  * Constructor Phase is default
  * @author TapasB
  */
 public Phase() {
 
 }
 
 /**
  * Method getRequestId returns requestId
  * @author TapasB
  * @return requestId
  */
 public int getRequestId() {
  return requestId;
 }
 
 /**
  * Method setRequestId sets the requestId
  * @author TapasB
  * @param requestId the requestId to set
  */
 public void setRequestId(int requestId) {
  this.requestId = requestId;
 }
 
 /**
  * Method getUrl returns url
  * @author TapasB
  * @return url
  */
 public String getUrl() {
  return url;
 }
 
 /**
  * Method setUrl sets the url
  * @author TapasB
  * @param url the url to set
  */
 public void setUrl(String url) {
  this.url = url;
 }
 
 /**
  * Method getStartTime returns startTime
  * @author TapasB
  * @return startTime
  */
 public Date getStartTime() {
  return startTime;
 }
 
 /**
  * Method setStartTime sets the startTime
  * @author TapasB
  * @param startTime the startTime to set
  */
 public void setStartTime(Date startTime) {
  this.startTime = startTime;
 }
 
 /**
  * Method getEndTime returns endTime
  * @author TapasB
  * @return endTime
  */
 public Date getEndTime() {
  return endTime;
 }
 
 /**
  * Method setEndTime sets the endTime
  * @author TapasB
  * @param endTime the endTime to set
  */
 public void setEndTime(Date endTime) {
  this.endTime = endTime;
 }
 
 /**
  * Method getPhaseId returns phaseId
  * @author TapasB
  * @return phaseId
  */
 public int getPhaseId() {
  return phaseId;
 }
 
 /**
  * Method setPhaseId sets the phaseId
  * @author TapasB
  * @param phaseId the phaseId to set
  */
 public void setPhaseId(int phaseId) {
  this.phaseId = phaseId;
 }
 
 /**
  * Method getPhaseName returns phaseName
  * @author TapasB
  * @return phaseName
  */
 public String getPhaseName() {
  return phaseName;
 }
 
 /**
  * Method setPhaseName sets the phaseName
  * @author TapasB
  * @param phaseName the phaseName to set
  */
 public void setPhaseName(String phaseName) {
  this.phaseName = phaseName;
 }
 
 /**
  * Method getPhaseName returns the name of the Phase
  * @author TapasB
  * @param phaseId
  * @return the phase name
  */
 public String getPhaseName(int phaseId) {
  if (phaseId == JSFLifecycle.INIT_CONTEXT_ID) {
   return "INIT_CONTEXT";
  } else if (phaseId == JSFLifecycle.PREPARE_MODEL_ID) {
   return "PREPARE_MODEL";
  } else if (phaseId == JSFLifecycle.APPLY_INPUT_VALUES_ID) {
   return "APPLY_INPUT_VALUES";
  } else if (phaseId == JSFLifecycle.VALIDATE_INPUT_VALUES_ID) {
   return "VALIDATE_INPUT_VALUES";
  } else if (phaseId == JSFLifecycle.PROCESS_UPDATE_MODEL_ID) {
   return "PROCESS_UPDATE_MODEL";
  } else if (phaseId == JSFLifecycle.VALIDATE_MODEL_UPDATES_ID) {
   return "VALIDATE_MODEL_UPDATES";
  } else if (phaseId == JSFLifecycle.PROCESS_COMPONENT_EVENTS_ID) {
   return "PROCESS_COMPONENT_EVENTS";
  } else if (phaseId == JSFLifecycle.METADATA_COMMIT_ID) {
   return "METADATA_COMMIT";
  } else if (phaseId == JSFLifecycle.PREPARE_RENDER_ID) {
   return "PREPARE_RENDER";
  } else if (phaseId == JSFLifecycle.JSF_RESTORE_VIEW_ID) {
   return "RESTORE_VIEW";
  } else if (phaseId == JSFLifecycle.JSF_APPLY_REQUEST_VALUES_ID) {
   return "JSF_APPLY_REQUEST_VALUES";
  } else if (phaseId == JSFLifecycle.JSF_PROCESS_VALIDATIONS_ID) {
   return "JSF_PROCESS_VALIDATIONS";
  } else if (phaseId == JSFLifecycle.JSF_UPDATE_MODEL_VALUES_ID) {
   return "JSF_UPDATE_MODEL_VALUES";
  } else if (phaseId == JSFLifecycle.JSF_INVOKE_APPLICATION_ID) {
   return "JSF_INVOKE_APPLICATION";
  } else {
   return "JSF_RENDER_RESPONSE";
  }
 }
}

有关详细信息,请查看here