[spring-projects/spring-boot]将 WebFluxTags#uri() 与 WebMvcTags#uri() 对齐

2024-04-17 946 views
2

你好,

此 PR 试图通过WebFluxTags#uri()WebMvcTags#uri().

让我知道你的想法。干杯,克里斯托夫

回答

0

因此,您将所有其他服务的 url 映射到rootunknown。是否可以在网关中收集其他服务的映射?用于在一个地方监视请求,因为可能有许多服务,并且每个服务可能有多个实例。

5

这不知道网关

7

@youzipi 这至少应该摆脱你看到的标签爆炸。如果你问我的话,将它与 WebMvcTags 的变体保持一致是有意义的。话虽如此,我理解您的担忧,unknown如果最佳匹配模式不可用,可能会出现更多 URL。但话又说回来,WebMvcTags 也有同样的问题,并且得到了类似的解决。

@bclozel 你觉得怎么样? (假设你自己分配了这个)

干杯

7

非常感谢@dreis2211!

@youzipi Spencer 和 Christoph 是对的,没有办法在不了解模式的情况下获得完整的 uri 信息,同时避免标签爆炸。我想网关应该更关心生成本地指标,并且每个应用程序都应该将自己的指标发布到共享存储库。

2

该代码在哪里可用?或者说有哪个版本的springBoot可用?

4

正如此问题所分配的里程碑所示,我们在 Spring Boot 2.0.8 中所做的更改。您还可以在关闭此 PR 的提交中看到包含更改的所有标签。

0

在找到这个之前,我刚刚发布了这个https://stackoverflow.com/questions/58724623/webfluxtagsuri-returning-wrong-tags 。也许您可以澄清一下 Exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);做了什么,以及必须做什么才能使请求始终在这里获得匹配,因为现在我的很多请求即使有路径,也会回到这里为空。

6

我正在使用 Spring boot Actuator 2.3.0 但仍然看到同样的问题。请尽快修复它,因为它阻止了我在 grafana 仪表板中的指标。

http_server_requests_seconds_bucket{异常=“无”,方法=“GET”,结果=“CLIENT_ERROR”,状态=“401” ,uri=“未知”,le=“22.906492245”,} 2.0

或者至少给我一些解决办法来解决这个问题。

@All - 有没有人覆盖这个 jar 以获得正确的 URL 而不是 Unknown ???

8

@veerumallavarapu你的问题与#16208非常相似 - 如果没有这个改变,你可能会遇到标签基数爆炸的情况:这会让你的grafana仪表板中的情况变得更糟(或更糟糕的是,OOM​​错误)。要解决此问题,请确保处理程序是按模式映射的。

有关您的特定案例的更多指导,请在 StackOverflow 上创建一个包含更多信息的问题,例如 HTTP 请求和处理请求的代码。

5

我没有得到“要解决此问题,请确保处理程序由模式映射。”我应该在哪里做这个?

3

@veerumallavarapu 原始指标数据并不能说明这一点。可能没有办法解决这个问题。这实际上取决于处理请求的内容。在 StackOverflow 上提问并提供所需的详细信息会更有成效。

9

难道不能再次设置模式吗?

1

有人可以澄清为什么需要进行此更改吗?有几个人声称它消除了标签爆炸,但 #14173 不是已经解决了吗?特别是以下 bean 应确保uri发出可配置的最大数量的不同值: https://github.com/spring-projects/spring-boot/blob/9460d74e8ad8c247c3ad9a627ab35aa08df21152/spring-boot-project/spring-boot-actuator-自动配置/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/web/reactive/WebFluxMetricsAutoConfiguration.java#L80

这是否是用户在注意到此问题时尚未升级到包含#14173 的 Spring 版本,从而导致仅相隔几个月就同一问题进行了两次不同的修复的情况?

将其置于现有实现之上有几个缺点并会导致各种问题:

  • 如果服务器路由器不使用模式,但仍然具有 定义的合理基数getMaxUriTags,则所有uri度量都会由于此更改而丢失。
  • 如果输入请求路径具有无限基数,则无法使用 Micrometer 的 MeterFilter 映射和替换函数提取具有有限基数的特定路径子元素。事实上,指标在一开始就丢失了,而不是被WebFluxMetricsAutoConfiguration下游过滤。例如,对于客户端指标,人们可以执行诸如 之类的操作MetricFilter.replaceTagValues​("uri", ...);
  • 继上一点之后,现在同一标签的 WebFlux 客户端和服务器指标之间的行为不一致uri
  • 发出UNKNOWN是令人困惑的(@veerumallavarapu 上面的评论强调了这一点,我的工程团队在这个问题上苦苦挣扎),由于服务器请求的 URI已知的,这个 PR 的实现只是决定将其标记为未知,如果在路由器功能。
  • 此行为是模糊且不可配置的。另一方面,WebFluxMetricsAutoConfiguration方法是可配置的,并在过滤启动时记录有用的消息。
  • 这是为每个服务器请求维护和执行的更多代码。可以说开销很小,但是尝试在代码库的不同位置两次限制 URI 标签爆炸感觉是错误的。

如果其他人同意这应该被恢复,我很乐意提交拉取请求。如果 #14173 和这个 PR 碰巧在某种程度上是相互排斥的,我也很高兴能得到启发。 :)

2

感谢您的反馈,@PyvesB。进行更改是为了使 WebFlux 指标的行为与 MVC 的行为保持一致。仪表过滤器和发射UNKNOWN并不真正等同于以前在达到限制时完全记录的停止请求指标。

发射UNKNOWN是默认值。可以通过提供WebFluxTagsContributor @Bean覆盖默认标签来更改它URI。或者,您可以提供自己的WebFluxTagsProvider @Bean标签并完全控制标签。

1

我们想要将 URI 添加到所有请求,因此我们添加了这个 Bean:

/**
 * The URI {@link io.micrometer.core.instrument.Tag} is occasionally set as <b>UNKNOWN</b> when the {@link HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE}
 * is not set in the exchange attributes. To counter that we added this custom WebFluxTagsContributor.
 */
@Bean
WebFluxTagsContributor uriTagContributorForConnectionProblems()
{
    return (exchange, ex) -> willExchangeUriBeingParsed(exchange)
                             ? Tags.empty()
                             : Tags.of("uri", exchange.getRequest().getPath().value());
}

private static boolean willExchangeUriBeingParsed(final ServerWebExchange exchange)
{
    return exchange.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE) != null;
}
0

谢谢@Illutax,这可行,但自 3.0.0 起,WebFluxTagsContributor 已被弃用 是否还有另一个面向未来的解决方案?

2

@sw-fox 正如 javadoc 中提到的,替换是ServerRequestObservationConvention.您可以在文档中了解有关如何使用此接口的更多信息。

5

我不知道我是否遇到了同样的问题,但是我的 Path 谓词都没有与 http_server requests *中的 uri 匹配,因此我创建了一个与配置中的所有 Path 谓词匹配的 ServerRequestObservationConvention bean,也许这可以帮助某人这个问题作为解决方法。

/**
 * This class is a workaround because spring cloud gateway is not setting the pathPattern to the context, which makes
 * metrics of known patterns have UNKNOWN uri.
 * <p>
 * This implementation uses the Path pattern predicates defined in {@link GatewayProperties}, so it probably won't work for
 * programmatically defined path predicates.
 */
@NonNullApi
@Component
public class PathMatchingObservationConvention extends DefaultServerRequestObservationConvention {
    private static final Logger LOGGER = LoggerFactory.getLogger(
            com.tripadvisor.dapr.gateway.PathMatchingObservationConvention.class);
    private final List<Tuple2<String, PathPattern>> pathPatterns;

    @Autowired
    public PathMatchingObservationConvention(final GatewayProperties gatewayProperties) {
        pathPatterns = new ArrayList<>();
        gatewayProperties.getRoutes().forEach(routeDefinition -> {
            pathPatterns.addAll(routeDefinition.getPredicates().stream().filter(
                    predicateDefinition -> "Path".equals(predicateDefinition.getName())).flatMap(
                    predicateDefinition -> predicateDefinition.getArgs().values().stream()).map(pattern -> {
                PathPatternParser parser = PathPatternParser.defaultInstance;
                pattern = parser.initFullPathPattern(pattern);
                return Tuple.of(pattern, parser.parse(pattern));
            }).toList());
        });
    }

    /**
     * Method overriden to set the pathPattern to the context based on the defined Path predicates in the configuration.
     * This makes 'uri' return matches patterns when getting the low cardinality tags.
     *
     * @param context the context
     * @return the uri
     * @see <a href="https://github.com/spring-projects/spring-boot/pull/15609#issuecomment-709987823">github issue</a>
     */
    @Override
    public KeyValues getLowCardinalityKeyValues(final ServerRequestObservationContext context) {
        if (context.getPathPattern() == null) {
            pathPatterns.stream().filter(
                    tuple -> tuple.map2(pattern -> pattern.matches(context.getCarrier().getPath()))._2()).map(
                    Tuple2::_1).findFirst().ifPresent(context::setPathPattern);
        }
        return super.getLowCardinalityKeyValues(context);
    }
}