如何使用过滤器来衡量性能?

6
我想知道能否使用过滤器(例如CorsFilter)来测量时间并将所花费的时间放到消息本身上。我知道以下代码是可行的:
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
  long startTime = System.nanoTime();
  chain.doFilter(request, response);
  long endTime = System.nanoTime();
  System.out.println("Time: " + (endTime - startTime) );
}

当然,这会输出总时间(以秒为单位)。我想把时间放到返回响应的头部,以便接收者可以查看头部并了解处理所需的时间。以下代码无法工作:

public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
  long startTime = System.nanoTime();
  if(response instanceof HttpServletResponse) {
    HttpServletResponse httpResp = (HttpServletResponse)response;
    httpResp.addHeader("Start-time", Long.toString(startTime));
  }

  chain.doFilter(request, response);
  long endTime = System.nanoTime();
  if(response instanceof HttpServletResponse) {
    HttpServletResponse httpResp = (HttpServletResponse)response;
    httpResp.addHeader("End-time", Long.toString(endTime));
  }
}
头信息仅包括开始时间,而不包括结束时间。我认为这是因为消息已经发送,所以修改对象将没有任何效果。


有没有通过使用过滤器将计时放入头信息的优雅/聪明解决方案?



谢谢,
菲尔



更新


我已经研究了使用HttpServletResponseWrapper来解决此问题。这仍然无法在响应标头中输出XXX-EndTime或YYY-EndTime。

@Override
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
  long startTime = System.nanoTime();

  if(response instanceof HttpServletResponse) {
    HttpServletResponse httpResp = (HttpServletResponse)response;
    httpResp.addHeader("Access-Control-Allow-Origin", "*");
    httpResp.addHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
    httpResp.addHeader("Allow", "GET, HEAD, OPTIONS");
    httpResp.addHeader("Access-Control-Allow-Headers", "*");
    httpResp.addHeader("A-Runtime", Long.toString(startTime));
  }

  OutputStream out = response.getOutputStream();

  GenericResponseWrapper wrapper = new GenericResponseWrapper((HttpServletResponse) response);

  chain.doFilter(request,wrapper);

  out.write(wrapper.getData());

  if(response instanceof HttpServletResponse) {
HttpServletResponse httpResp = (HttpServletResponse)response;
httpResp.addHeader("XXX-EndTime", Long.toString(System.nanoTime() - startTime));

    wrapper.addHeader("YYY-EndTime", Long.toString(System.nanoTime() - startTime));
  }

  out.close();
}
1个回答

2
请看 HttpServletResponseWrapper

好的,下面是代码:

这段代码以两种方式缓冲输出,因此在伪输出之后添加标题。实际上,addHeader可以通过输出来实现,所以我们很幸运它能工作。边界情况的代码。如果不幸的话,addHeader将不得不被覆盖。

请注意,在我的测试应用程序中,只调用了getOutputStream。需要选择getPrintWriter或getOutputStream。

private static class PostponingResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream bos;
    private ServletOutputStream outputStream;
    private StringWriter sw;
    private PrintWriter printWriter;
    private boolean usingOutputStream;
    private boolean usingWriter;

    public PostponingResponseWrapper (HttpServletResponse response) {
        super(response);
        bos = new ByteArrayOutputStream();
        outputStream = new ServletOutputStream() {
            @Override
            public void write(int b) throws IOException {
                bos.write(b);
            }
        };
        sw = new StringWriter();
        printWriter = new PrintWriter(sw);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        usingWriter = true;
        LOGGER.info("getWriter usingWriter {}, usingOutputStream {}", usingWriter, usingOutputStream);
        return printWriter;
    }

    @Override
    public void flushBuffer() throws IOException {
        LOGGER.info("flushBuffer");
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        usingOutputStream = true;
        LOGGER.info("getOutputStream usingWriter {}, usingOutputStream {}", usingWriter, usingOutputStream);
        ServletOutputStream out = new ServletOutputStream() {
            @Override
            public void write(int b) throws IOException {
                outputStream.write(b);
            }
        };
        return out;
    }

    public void finish() throws IOException {
        LOGGER.info("finish");
        if (usingWriter) {
            super.getWriter().print(sw.toString());
        } else if (usingOutputStream) {
            super.getOutputStream().write(bos.toByteArray());
        }
    }
}

public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    PostponingResponseWrapper responseWrapper =
            new PostponingResponseWrapper (httpServletResponse);
    responseWrapper.addHeader("Before", "Already-Worked");
    chain.doFilter(request, responseWrapper);
    responseWrapper.addHeader("After", "And-Now-This");
    responseWrapper.finish(); // Writes the actual response
}

@YevgeniyM。你说得对,应该只是一个注释,说明有这样的包装类+1。 - Joop Eggen
好的,我已经使用HttpServletResponseWrapper实现了一个解决方案,但仍然无法使其工作!我将使用我的新代码更新上面的问题。 - Phil
@YevgeniyM. 添加了实际代码,因为你对原始答案是正确的。 - Joop Eggen
1
@JoopEggen 你的努力将会得到回报 :) - Yevgeniy
嗨,Joop。谢谢你的代码。我已经使用了你提供的代码变体来解决我的问题。谢谢。 - Phil

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