[spring-projects/spring-boot]为 Spring Data REST 存储库提供更细粒度的指标

2024-04-17 404 views
4

问题:当将 Spring Boot Actuator 与 data-rest 结合使用并公开 webmvc 指标时,所有 REST 数据存储库的请求统计时间都会汇总,因为它们的端点表示为/api/{repository}/

预期的:

每个 Spring Data Rest 存储库端点都由单独的指标表示,因为在识别性能问题时,所有存储库的汇总数据毫无意义。例如,统计数据需要与存储库指标/api/person分开。/api/house

环境:Spring boot 5.0.7

回答

8

感谢您的报告。这是一个有趣的问题。我不确定仅在 Spring Boot 中我们可以做很多事情,因为它需要详细了解 Spring Data REST 的 URI 结构并了解路径中每个参数可能的基数。例如,您需要/api/person和 的单独统计数据api/house,但不希望/api/house/1, /api/house/2, ...,的单独统计数据/api/house/12345

/api/{repository}/目前,我们从 Spring MVC 设置的请求属性中获取匹配的路径模式(在本例中)。也许 Spring Data REST 可以设置自己的更详细的属性。诸如/api/house//api/personapi/house/{id}和 之类的东西api/person/{id}

你觉得@olivergierke 和@mp911de 怎么样?

8

我暂时的丑陋的解决方法是这样的,也许数据休息中的自定义标签提供者可以解决这个问题:

    /**
     * We are modifying the tags exposed by the webmvc metrics
     */
    @Bean
    public WebMvcTagsProvider webMvcTagsProvider() {
        return new DefaultWebMvcTagsProvider() {
            @Override
            public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler, Throwable exception) {
                return StreamSupport.stream(super.getTags(request, response, handler, exception).spliterator(), false)
                        .map(it -> it.getKey().equals("uri") ? mapUriTag(it, request) : it).collect(Collectors.toList());
            }
        };
    }

    /**
     * Modify a URI tag provided by spring metrics
     * By default spring resolves REST data repositories as {repository} in the uri
     * We substitute this and other templated URI parts with the real URI so metrics are exposed on a repository basis instead.
     * We blacklist some templated parts from substitution like "id" as they belong to the same endpoint
     */
    private static Tag mapUriTag(Tag tag, HttpServletRequest request) {
        List<String> replaceBlackList = Arrays.asList("{id}");
        StringTokenizer templatePathTokenizer = new StringTokenizer(tag.getValue(),"/");
        StringTokenizer realPathTokenizer = new StringTokenizer(request.getServletPath(),"/");
        StringBuilder finalPath = new StringBuilder();

        while(templatePathTokenizer.hasMoreTokens()) {
            String templatePathToken = templatePathTokenizer.nextToken();
            String realPathToken = realPathTokenizer.hasMoreTokens() ? realPathTokenizer.nextToken() : null;
            finalPath.append("/");
            if (templatePathToken.startsWith("{") && templatePathToken.endsWith("}")
                    && !replaceBlackList.contains(templatePathToken) && realPathToken != null) {
                finalPath.append(realPathToken);
            }
            else {
                finalPath.append(templatePathToken);
            }
        }
        return new ImmutableTag("uri",finalPath.toString());
    } 
6

的使用$basePath/{repository}源于这样一个事实:到目前为止,这是注册控制器的最方便的方法,最终将注册“多个”映射。实际上,它只是一个,我们在解析处理程序映射等时确定在调用时使用的存储库。

我们还使用占位符根据调用方法上的注释{repository}来解析实现中的存储库。我认为如果我们转而避免使用这些注释并单独添加显式映射,情况会变得更加复杂。HandlerMethodArgumentResolver@RequestMapping

摆脱该模型可能不那么容易的另一个原因是用户当前可以使用{repository}占位符来覆盖全局资源。我认为我们会破坏该功能并转向显式映射。

我会咨询@rstoyanchev,但恐怕这(如果可能的话)不会是一个快速解决方案。我想知道与此同时,指标 API 中是否有一个钩子,由 Spring Data REST 自动配置注册的某些组件可以使用该钩子来创建不同的存储桶。即类似于 @high-stakes 的东西,但可以使用 Spring Data REST 内部元数据更加集中。

5

感谢您的浏览,@olivergierke。

我不建议更改任何 Spring Data REST 的映射。我想知道 Spring Data REST 是否可以在请求处理期间在解析存储库后设置一个带有路径模式的请求属性。该模式可能包含比 Spring MVC 更多的信息,因为它可以用{repository}已解决的存储库的具体值替换占位符。也许,它还可以为每个存储库提供的各种资源向模式添加更多组件,而仅保留{id}模式的高基数部分等。

4

换句话说,我建议:

可以通过 Spring Data REST 完成并将结果放置在请求属性中。

7

不确定这是否有帮助,但从 5.1 开始,可以通过外部配置(通过WebMvcConfigurer)为控制器分配基本路径,请参阅SPR-16336docs

此外,从 4.2 开始,出现了一个公共 API,可以通过编程方式注册和取消注册控制器方法,请参阅SPR-11541docs

1

我已将DATAREST-1294归档、修复并向后移植到 Lovelace 和 Kay 维护分支。我们现在公开一个EFFECTIVE_REPOSITORY_LOOKUP_PATH请求属性,以包含已部分解析PathPattern的存储库基础解析,即,将有效地为每个公开的存储库生成不同的模式。

根据 @rstoyanchev 的建议,我还提交了DATAREST-1295来调查我们是否可以放弃自己的基本路径处理,转而使用 Spring MVC。

1

好东西。谢谢你,@olivergierke。我们需要等待一段时间才能实现这一点,因为在 Boot 2.1.0.RELEASE 之前没有计划发布 Spring Data 版本系列。

0

如果您愿意在 RC1 之后升级到错误修复版本,我们可以轻松及时发布一个版本。

0

是的,我们很乐意这样做。 RC 后的错误修复就很好了。你们也能运送凯发布列车吗?这将使我们能够同时在 2.0.x 中执行此操作。我认为 2.0.x 中的更改是有必要的,因为它的风险较低,而且由于指标相当无用,当前的行为是否实际上可以被视为错误还存在争议。

2

我将安排下周周五发布 Kay 和 Lovelace 服务。 /抄送@mp911de

5

做得好!我想知道替换 {search} 和类似的模式({projection},{id}​​ 除外)是否也有意义。缺点:一个存储库端点可以有很多指标 优点:这些指标与不同类型的查询具有相同的粒度。我的findByFirstName查询可能很快,但findByLastName由于字段没有索引等而非常慢。由于这些更多的是 RPC 风格的调用,我认为区分是有意义的

你怎么认为?

4

我会听从@olivergierke 的意见。支持它的任何更改都将在 Spring Data REST 中进行,因此 DATAREST-1294可能是继续讨论的最佳位置。