java—如何在spring boot中保存对数据库的请求和响应

b4wnujal  于 2021-06-29  发布在  Java
关注(0)|答案(3)|浏览(503)

我想写一个方面或者类似的东西,每当一个请求到达控制器,它就会将请求和响应保存到数据库中。
第一个问题是我应该在实体中使用什么类型的请求和响应(字符串、blob等)
第二个问题,如何获取请求、响应及其控制器名称来创建要保存到数据库的实体?
最后,是否可以计算控制器的响应时间(在控制器中花费的时间)?

wmvff8tz

wmvff8tz1#

第一个问题是我应该在实体中使用什么类型的请求和响应(字符串、blob等)
它主要取决于数据库供应商和请求/响应长度。
对于某些供应商,字符串可能会受到限制,因此需要blob。另一方面,blob上的匹配速度较慢。
另一种选择是使用nosql格式,比如json。
第二个问题,如何获取请求、响应及其控制器名称来创建要保存到数据库的实体?
其实有好几种方法。
您可以利用内置的SpringBootHTTP跟踪特性,但它有一个限制:请求/响应的发布/接收不可用。
5.8. http跟踪
可以通过在应用程序的配置中提供httptracerepository类型的bean来启用http跟踪。为了方便起见,SpringBoot提供了一个inmemoryhttptracerepository,默认情况下,它存储最后100次请求-响应交换的跟踪。inmemoryhttptracerepository与其他跟踪解决方案相比是有限的,我们建议只在开发环境中使用它。对于生产环境,建议使用生产就绪跟踪或可观察性解决方案,如zipkin或springcloudsleuth。或者,创建满足您需要的httptracerepository。
httptrace端点可用于获取有关存储在httptracerepository中的请求-响应交换的信息。
5.8.1. 自定义http跟踪
要自定义每个跟踪中包含的项,请使用management.trace.http.include配置属性。对于高级定制,请考虑注册自己的httpexchangetracer实现。
另一种方法是为请求/响应实现一个过滤器并登录它。
例如:

@Component
public class RequestResponseStoringFilter implements Filter {

  @Override
  public void doFilter(ServletRequest req, ServletResponse res,
      FilterChain chain) throws IOException, ServletException {

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    long start = System.currentTimeMillis();
    try {
        chain.doFilter(req, resp);
    } finally {
       // Measure elapsed time
        long elapsed = System.currentTimeMillis() - start;
       // store request data and store response data in a DB
       .....
    }

  }

  @Override
  public void destroy() {}

  @Override
  public void init(FilterConfig arg0) throws ServletException {}

}

最后,是否可以计算控制器的响应时间(在控制器中花费的时间)?
实现过滤器的方法如上所示。
httptrace端点方式提供了 timeTaken 现场。
fiy,以下是httptrace示例的内容:

HttpTrace.Principal getPrincipal() 
HttpTrace.Request   getRequest() 
HttpTrace.Response  getResponse() 
HttpTrace.Session   getSession() 
Instant getTimestamp() 
Long    getTimeTaken()
jdzmm42g

jdzmm42g2#

这是对其他答案的扩展,但我认为它需要一个单独的答案。
如果您正在拉入springbootstarterweb,那么您已经在拉入springaop了。如果你要走点切割路线,我强烈建议你只使用测微计 @Timed 随附的注解 spring-boot-starter-actuator . 我已经写了很多次我自己的度量切入点,但是如果你只是在计时和成功与失败的计数之后, @Timed 效果很好。
我还强烈建议使用时间序列数据库(例如inflox)来存储响应时间和其他性能指标。将原始有效负载和其他可能的审计问题保存在单独的数据库中。有一些非常强大的事情,你可以做的涌入和运行格拉法纳或chronograf之上。毫无疑问,我现在的公司多年来做的最好的事情之一就是采用influx/chronograf。
关于请求/响应捕获,我的工作流程中有一个奇怪的边缘案例,其中http跟踪无法满足某些硬需求。您可以使用 ContentCachingRequestWrapper 然后您可以通过以下方式访问它们:

@Component
class MyPayloadCapturingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper(request)
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response)

        filterChain.doFilter(requestWrapper, responseWrapper)

        def requestBody = new String(requestWrapper.contentAsByteArray)
        def responseBody = new String(responseWrapper.contentAsByteArray)
        //..do something with them
    }
}

注意 OncePerRequestFilter ,我发现我的筛选器多次为同一请求触发。这就避免了这种情况。

i2loujxw

i2loujxw3#

添加此依赖项:

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

然后,创建一个 Around 控制器方法执行方面:

@Around("within(path.to.your.controller.*)")
    public Object pointcutWithin(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info(" ###### pointcutWithin() before");
        long start = System.nanoTime();

        Object result = joinPoint.proceed();

        logger.info(" ###### pointcutWithin() after");
        long end = System.nanoTime();

        long timeElapsedInMillis = (end - start) / 1000000;
        logger.info(" ###### elapsed time in millis: "+timeElapsedInMillis);

        return result;
    }

至于持久化:首先,得到req和resp,如下所示:

MyRequest req = (MyRequest) joinPoint.getArgs()[0];

MyResponse resp = (MyResponse) result;

那就看你到底想坚持什么了。对于具有简单字段的类,我会使用 varchar ,记住要覆盖他们的 toString 方法。

相关问题