Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to disable RouteRefreshListener #2958

Closed
DergachevDenis opened this issue May 19, 2023 · 3 comments
Closed

Add option to disable RouteRefreshListener #2958

DergachevDenis opened this issue May 19, 2023 · 3 comments
Milestone

Comments

@DergachevDenis
Copy link

DergachevDenis commented May 19, 2023

My Gateway is connected to Eureka Service Discovery. Every time a gateway client refresh its state in Eureka, all the routes in gateway are rebuilt. This is critical for me since I have redefined Route Locator and am downloading my routes from another service. How can I disable this behavior or change it?
The whole problem is org.springframework.cloud.gateway.route.RouteRefreshListener. When eureka client creates a HeartbeatEvent, it triggers the rebuilding of routes, is there any way to override this Bean? It does not implement the interface((

@Bean
    @ConditionalOnClass(
            name = "org.springframework.cloud.client.discovery.event.HeartbeatMonitor")
    public RouteRefreshListener routeRefreshListener(
            ApplicationEventPublisher publisher) {
        return new RouteRefreshListener(publisher);
    }

public class RouteRefreshListener implements ApplicationListener<ApplicationEvent> {

    private final ApplicationEventPublisher publisher;

    private HeartbeatMonitor monitor = new HeartbeatMonitor();

    public RouteRefreshListener(ApplicationEventPublisher publisher) {
        Assert.notNull(publisher, "publisher may not be null");
        this.publisher = publisher;
    }

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent) event;
            if (!WebServerApplicationContext.hasServerNamespace(
                    refreshedEvent.getApplicationContext(), "management")) {
                reset();
            }
        }
        else if (event instanceof RefreshScopeRefreshedEvent
                || 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));
    }```
@DergachevDenis
Copy link
Author

DergachevDenis commented May 25, 2023

I did it using the BeanPostProcessor

@Component
public class GatewayPostProcessor implements BeanPostProcessor, Ordered {

    private final ApplicationEventPublisher publisher;

    public GatewayPostProcessor(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (beanName.equals("routeRefreshListener")) {
            return new RouteRefreshListenerCustom(publisher);
        } else {
            return bean;
        }
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

@apollyon4
Copy link

apollyon4 commented Nov 21, 2023

This issue is not only bad because a route filter bean is created each time a HeartbeatEvent is published, but it also causes filters that require initialization, such as LocalResponseCacheGatewayFilterFactory, to behave incorrectly.
So I think the state of the HeartbeatEvent should only change when the category changes, rather than simply incrementing the counter.

+I'm not sure why I need to publish a RefreshRoutesEvent when a HeartbeatEvent is published. Are there filters that need to be created when the catalog changes?

@spencergibb spencergibb changed the title Unplanned refresh routes when use Spring Eureka Client in Spring Cloud Gateway Add option to disable RouteRefreshListener Mar 12, 2024
@spencergibb spencergibb added this to the 4.1.2 milestone Mar 12, 2024
@spencergibb
Copy link
Member

spring.cloud.gateway.route-refresh-listener.enabled=false

@github-project-automation github-project-automation bot moved this to Done in 2023.0.1 Mar 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Status: Done
Development

No branches or pull requests

3 participants