【问题标题】:How to save requests and responses to database in spring boot如何在spring boot中保存对数据库的请求和响应
【发布时间】:2021-04-07 22:26:36
【问题描述】:

我想写一个方面或类似的东西,每当请求到达控制器时,它就会将请求和响应保存到数据库。

第一个问题是我应该在我的实体中使用什么类型来请求和响应(字符串、blob 等)

第二个问题,如何获取request、response及其控制器名来创建实体保存到数据库?

最后,是否可以计算控制器的响应时间(在控制器中花费的时间)?

【问题讨论】:

    标签: java spring spring-boot logging aop


    【解决方案1】:

    添加这个依赖:

    <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 方法即可。

    【讨论】:

    • 如何在这个方法中获取请求和响应作为字符串?
    • 我已将其添加到我的答案中。
    【解决方案2】:

    第一个问题是我应该在我的实体中使用什么类型来请求和 响应(字符串、blob 等)

    主要取决于数据库供应商和请求/响应长度。
    某些供应商可能会限制字符串,因此需要 blob。 另一方面,在 blob 上的匹配速度较慢。
    另一种选择是使用 nosql 格式,例如 JSON。

    第二个问题,如何获取request、response及其控制器名 创建要保存到数据库的实体?

    确实有几种方法。
    您可以利用内置的 Spring Boot http tracing features,但它有一个限制:发布/接收的请求/响应不可用。

    5.8。 HTTP 跟踪

    可以通过提供一个 bean 类型来启用 HTTP 跟踪 应用程序配置中的 HttpTraceRepository。为了 为了方便,Spring Boot 提供了一个 InMemoryHttpTraceRepository 默认情况下,存储最近 100 次请求-响应交换的跟踪。 与其他跟踪相比,InMemoryHttpTraceRepository 受到限制 解决方案,我们建议仅将其用于开发环境。 对于生产环境,使用生产就绪跟踪或 可观察性解决方案,例如 Zipkin 或 Spring Cloud Sleuth,是 推荐的。或者,创建您自己的 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() 即时获取时间戳() 长 getTimeTaken()

    【讨论】:

      【解决方案3】:

      这有点扩展其他答案,但我认为它需要一个单独的答案。

      如果您引入了 spring-boot-starter-web,那么您已经引入了 spring-aop。但是,如果您要走切入点路线,我强烈建议您只使用 spring-boot-starter-actuator 附带的 Micrometer @Timed 注释。我已经多次编写了自己的度量切入点,但如果您只是在计时和成功和失败的次数之后,@Timed 效果很好。

      我还强烈建议研究使用时间序列数据库(例如 influx)来存储响应时间和其他性能指标等内容。将原始有效负载和其他可能的审计问题保存在单独的数据库中。您可以使用 influx 做一些非常强大的事情,并在其上运行 GrafanaChronograf。毫无疑问,我目前的公司多年来所做的最好的事情之一就是采用 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,我发现我的过滤器多次触发同一请求。这可以防止这种情况发生。

      【讨论】:

      • 首先,感谢您的回答,它有效。但是,我不知道如何获得响应时间?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 2017-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多