Merge branch '3.0.x'
This commit is contained in:
@@ -2119,6 +2119,10 @@ spring:
|
||||
|
||||
For some usages of the gateway, properties are adequate, but some production use cases benefit from loading configuration from an external source, such as a database. Future milestone versions will have `RouteDefinitionLocator` implementations based off of Spring Data Repositories, such as Redis, MongoDB, and Cassandra.
|
||||
|
||||
=== RouteDefinition Metrics
|
||||
|
||||
To enable `RouteDefinition` metrics, add spring-boot-starter-actuator as a project dependency. Then, by default, the metrics will be available as long as the property `spring.cloud.gateway.metrics.enabled` is set to `true`. A gauge metric named `spring.cloud.gateway.routes.count` will be added, whose value is the number of `RouteDefinitions`. This metric will be available from `/actuator/metrics/spring.cloud.gateway.routes.count`.
|
||||
|
||||
== Route Metadata Configuration
|
||||
|
||||
You can configure additional parameters for each route by using metadata, as follows:
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.gateway.filter.GatewayMetricsFilter;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionMetrics;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayHttpTagsProvider;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayRouteTagsProvider;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayTagsProvider;
|
||||
@@ -71,4 +73,12 @@ public class GatewayMetricsAutoConfiguration {
|
||||
return new GatewayMetricsFilter(meterRegistry, tagsProviders, properties.getPrefix());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(MeterRegistry.class)
|
||||
@ConditionalOnProperty(name = GatewayProperties.PREFIX + ".metrics.enabled", matchIfMissing = true)
|
||||
public RouteDefinitionMetrics routeDefinitionMetrics(MeterRegistry meterRegistry,
|
||||
RouteDefinitionLocator routeDefinitionLocator, GatewayMetricsProperties properties) {
|
||||
return new RouteDefinitionMetrics(meterRegistry, routeDefinitionLocator, properties.getPrefix());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.cloud.gateway.filter.GatewayMetricsFilter;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
||||
/**
|
||||
* A metric to track the number of routes definitions on the gateway.
|
||||
*
|
||||
* @author Fredrich Ombico
|
||||
*/
|
||||
public class RouteDefinitionMetrics implements ApplicationListener<RefreshRoutesEvent> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(GatewayMetricsFilter.class);
|
||||
|
||||
private final RouteDefinitionLocator routeLocator;
|
||||
|
||||
private final AtomicInteger routeDefinitionCount;
|
||||
|
||||
private final String metricsPrefix;
|
||||
|
||||
public RouteDefinitionMetrics(MeterRegistry meterRegistry, RouteDefinitionLocator routeLocator,
|
||||
String metricsPrefix) {
|
||||
this.routeLocator = routeLocator;
|
||||
|
||||
if (metricsPrefix.endsWith(".")) {
|
||||
this.metricsPrefix = metricsPrefix.substring(0, metricsPrefix.length() - 1);
|
||||
}
|
||||
else {
|
||||
this.metricsPrefix = metricsPrefix;
|
||||
}
|
||||
routeDefinitionCount = meterRegistry.gauge(this.metricsPrefix + ".routes.count", new AtomicInteger(0));
|
||||
}
|
||||
|
||||
public String getMetricsPrefix() {
|
||||
return metricsPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(RefreshRoutesEvent event) {
|
||||
routeLocator.getRouteDefinitions().count().subscribe(count -> {
|
||||
routeDefinitionCount.set(count.intValue());
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("New routes count: " + routeDefinitionCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.gateway.filter.GatewayMetricsFilter;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionMetrics;
|
||||
import org.springframework.cloud.gateway.support.tagsprovider.GatewayTagsProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -48,6 +49,9 @@ public class GatewayMetricsAutoConfigurationTests {
|
||||
@Autowired(required = false)
|
||||
private GatewayMetricsFilter filter;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RouteDefinitionMetrics routeDefinitionMetrics;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<GatewayTagsProvider> tagsProviders;
|
||||
|
||||
@@ -58,6 +62,12 @@ public class GatewayMetricsAutoConfigurationTests {
|
||||
assertThat(tagsProviders).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void routeDefinitionMetricsBeanExists() {
|
||||
assertThat(routeDefinitionMetrics).isNotNull();
|
||||
assertThat(routeDefinitionMetrics.getMetricsPrefix()).isEqualTo("spring.cloud.gateway");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@@ -67,11 +77,19 @@ public class GatewayMetricsAutoConfigurationTests {
|
||||
@Autowired(required = false)
|
||||
private GatewayMetricsFilter filter;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RouteDefinitionMetrics routeDefinitionMetrics;
|
||||
|
||||
@Test
|
||||
public void gatewayMetricsBeanMissing() {
|
||||
assertThat(filter).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void routeDefinitionMetricsBeanMissing() {
|
||||
assertThat(routeDefinitionMetrics).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@@ -82,6 +100,9 @@ public class GatewayMetricsAutoConfigurationTests {
|
||||
@Autowired(required = false)
|
||||
private GatewayMetricsFilter filter;
|
||||
|
||||
@Autowired(required = false)
|
||||
private RouteDefinitionMetrics routeDefinitionMetrics;
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<GatewayTagsProvider> tagsProviders;
|
||||
|
||||
@@ -92,6 +113,12 @@ public class GatewayMetricsAutoConfigurationTests {
|
||||
assertThat(tagsProviders).extracting("class").contains(CustomTagsProviderConfig.EmptyTagsProvider.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void routeDefinitionMetricsBeanExists() {
|
||||
assertThat(routeDefinitionMetrics).isNotNull();
|
||||
assertThat(routeDefinitionMetrics.getMetricsPrefix()).isEqualTo("myprefix");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Fredrich Ombico
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class RouteDefinitionMetricsTests {
|
||||
|
||||
@Mock
|
||||
private MeterRegistry registry;
|
||||
|
||||
@Mock
|
||||
private RouteDefinitionLocator routeDefinitionLocator;
|
||||
|
||||
private RouteDefinitionMetrics routeDefinitionMetrics;
|
||||
|
||||
private AtomicInteger routeDefinitionCount;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
routeDefinitionCount = new AtomicInteger(0);
|
||||
when(registry.gauge(any(String.class), any(AtomicInteger.class))).thenReturn(routeDefinitionCount);
|
||||
routeDefinitionMetrics = new RouteDefinitionMetrics(registry, routeDefinitionLocator, "prefix.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void metricsPrefix() {
|
||||
assertThat(routeDefinitionMetrics.getMetricsPrefix()).isEqualTo("prefix");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReportOneRoute() {
|
||||
List<RouteDefinition> oneRoute = Collections.singletonList(new RouteDefinition());
|
||||
when(routeDefinitionLocator.getRouteDefinitions()).thenReturn(Flux.fromStream(oneRoute.stream()));
|
||||
|
||||
RefreshRoutesEvent refreshRoutesEvent = new RefreshRoutesEvent(this);
|
||||
routeDefinitionMetrics.onApplicationEvent(refreshRoutesEvent);
|
||||
|
||||
assertThat(routeDefinitionCount.get()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReportMultipleRoutes() {
|
||||
List<RouteDefinition> multipleRoutes = Arrays.asList(new RouteDefinition(), new RouteDefinition(),
|
||||
new RouteDefinition(), new RouteDefinition(), new RouteDefinition());
|
||||
when(routeDefinitionLocator.getRouteDefinitions()).thenReturn(Flux.fromStream(multipleRoutes.stream()));
|
||||
|
||||
RefreshRoutesEvent refreshRoutesEvent = new RefreshRoutesEvent(this);
|
||||
routeDefinitionMetrics.onApplicationEvent(refreshRoutesEvent);
|
||||
|
||||
assertThat(routeDefinitionCount.get()).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReportZeroIfNoRoutes() {
|
||||
RouteDefinition[] zeroRoutes = new RouteDefinition[0];
|
||||
when(routeDefinitionLocator.getRouteDefinitions()).thenReturn(Flux.fromArray(zeroRoutes));
|
||||
|
||||
RefreshRoutesEvent refreshRoutesEvent = new RefreshRoutesEvent(this);
|
||||
routeDefinitionMetrics.onApplicationEvent(refreshRoutesEvent);
|
||||
|
||||
assertThat(routeDefinitionCount.get()).isEqualTo(0);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user