Adds listener to publish RefreshRoutesEvent when needed.
It will publish on InstanceRegisteredEvent and if a HeartbeatMonitor decides if an update is needed based on a HeartbeatEvent. fixes gh-236
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -27,7 +27,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.route.RefreshRoutesEvent;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
|
||||
@@ -79,7 +79,6 @@ public class GatewayControllerEndpoint implements ApplicationEventPublisherAware
|
||||
|
||||
// TODO: Add uncommited or new but not active routes endpoint
|
||||
|
||||
//TODO: this should really be a listener that responds to a RefreshEvent
|
||||
@PostMapping("/refresh")
|
||||
public Mono<Void> refresh() {
|
||||
this.publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||
|
||||
@@ -90,7 +90,9 @@ import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.RouteRefreshListener;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -244,10 +246,16 @@ public class GatewayAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
//TODO: property to disable composite?
|
||||
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
|
||||
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
|
||||
return new RouteRefreshListener(publisher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
|
||||
return new FilteringWebHandler(globalFilters);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.gateway.route;
|
||||
package org.springframework.cloud.gateway.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -20,9 +20,11 @@ package org.springframework.cloud.gateway.route;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2017 the original author or authors.
|
||||
* Copyright 2013-2018 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.
|
||||
@@ -20,11 +20,12 @@ package org.springframework.cloud.gateway.route;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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 org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
|
||||
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
|
||||
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
// see ZuulDiscoveryRefreshListener
|
||||
// TODO: make abstract class in commons?
|
||||
public class RouteRefreshListener
|
||||
implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
private HeartbeatMonitor monitor = new HeartbeatMonitor();
|
||||
private final ApplicationEventPublisher publisher;
|
||||
|
||||
public RouteRefreshListener(ApplicationEventPublisher publisher) {
|
||||
Assert.notNull(publisher, "publisher may not be null");
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
if (event instanceof InstanceRegisteredEvent) {
|
||||
reset();
|
||||
}
|
||||
else if (event instanceof ParentHeartbeatEvent) {
|
||||
ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
|
||||
resetIfNeeded(e.getValue());
|
||||
}
|
||||
else if (event instanceof HeartbeatEvent) {
|
||||
HeartbeatEvent e = (HeartbeatEvent) event;
|
||||
resetIfNeeded(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void resetIfNeeded(Object value) {
|
||||
if (this.monitor.update(value)) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
this.publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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
|
||||
*
|
||||
* http://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 org.junit.Test;
|
||||
|
||||
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
|
||||
import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent;
|
||||
import org.springframework.cloud.client.discovery.event.ParentHeartbeatEvent;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
public class RouteRefreshListenerTests {
|
||||
|
||||
@Test
|
||||
public void onInstanceRegisteredEvent() {
|
||||
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
|
||||
RouteRefreshListener listener = new RouteRefreshListener(publisher);
|
||||
|
||||
listener.onApplicationEvent(new InstanceRegisteredEvent<>(this, new Object()));
|
||||
|
||||
verify(publisher).publishEvent(any(RefreshRoutesEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onHeartbeatEvent() {
|
||||
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
|
||||
RouteRefreshListener listener = new RouteRefreshListener(publisher);
|
||||
|
||||
listener.onApplicationEvent(new HeartbeatEvent(this, 1L));
|
||||
listener.onApplicationEvent(new HeartbeatEvent(this, 1L));
|
||||
listener.onApplicationEvent(new HeartbeatEvent(this, 2L));
|
||||
|
||||
verify(publisher, times(2)).publishEvent(any(RefreshRoutesEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onParentHeartbeatEvent() {
|
||||
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
|
||||
RouteRefreshListener listener = new RouteRefreshListener(publisher);
|
||||
|
||||
listener.onApplicationEvent(new ParentHeartbeatEvent(this, 1L));
|
||||
listener.onApplicationEvent(new ParentHeartbeatEvent(this, 1L));
|
||||
listener.onApplicationEvent(new ParentHeartbeatEvent(this, 2L));
|
||||
|
||||
verify(publisher, times(2)).publishEvent(any(RefreshRoutesEvent.class));
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,10 @@
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-gateway-core</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user