Spring Zuul: Dynamically disable a route to a service
Asked Answered
U

3

2

I'm trying to disable a Zuul route to a microservice registered with Eureka at runtime (I'm using spring boot).

This is an example:

localhost/hello
localhost/world

Those two are the registered microservices. I would like to disable the route to one of them at runtime without shutting it down.

Is there a way to do this?

Thank you,

Nano

Unrefined answered 24/10, 2016 at 14:2 Comment(1)
By dynamically you mean via code... if you are using cloud config then you can remove the routing from zuul properties and refresh it via actuator then the routing will not happen... that sounds dynamic to me...Trite
U
0

After a lot of efforts I came up with this solution. First, I used Netflix Archaius to watch a property file. Then I proceeded as follows:

public class ApplicationRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

public ApplicationRouteLocator(String servletPath, ZuulProperties properties) {
    super(servletPath, properties );
}


@Override
public void refresh() {
   doRefresh();
}
}

Made the doRefresh() method public by extending SimpleRouteLocator and calling its method in the overridden one of the interface RefreshableRouteLocator.

Then I redefined the bean RouteLocator with my custom implementation:

@Configuration
@EnableConfigurationProperties( { ZuulProperties.class } )
public class ZuulConfig {

public static ApplicationRouteLocator simpleRouteLocator;

@Autowired
private ZuulProperties zuulProperties;

@Autowired
private ServerProperties server;

@Bean
@Primary
public RouteLocator routeLocator() {
    logger.info( "zuulProperties are: {}", zuulProperties );
    simpleRouteLocator = new ApplicationRouteLocator( this.server.getServletPrefix(),
            this.zuulProperties );


    ConfigurationManager.getConfigInstance().addConfigurationListener( configurationListener );

    return simpleRouteLocator;
}


private ConfigurationListener configurationListener =
        new ConfigurationListener() {

            @Override
            public void configurationChanged( ConfigurationEvent ce ) {

                            // zuulProperties.getRoutes() do something
                            // zuulProperties.getIgnoredPatterns() do something
                            simpleRouteLocator.refresh();
                        }



                }

}

Every time a property in the file was modified an event was triggered and the ConfigurationEvent was able to deal with it (getPropertyName() and getPropertyValue() to extract data from the event). Since I also Autowired the ZuulProperties I was able to get access to it. With the right rule I could find whether the property of Zuul

zuul.ignoredPatterns

was modified changing its value in the ZuulProperties accordingly.

Unrefined answered 24/5, 2017 at 9:11 Comment(1)
Cannot resolve method 'getServletPrefix' in 'ServerProperties'Operculum
J
1

Alternatively to using Cloud Config, custom ZuulFilter can be used. Something like (partial implementation to show the concept):

public class BlackListFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "pre";
    }
    ...
    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        String uri = ctx.getRequest().getRequestURI();
        String appId = uri.split("/")[1];
        if (blackList.contains(appId)) {
            ctx.setSendZuulResponse(false);
            LOG.info("Request '{}' from {}:{} is blocked",
                    uri, ctx.getRequest().getRemoteHost(), ctx.getRequest().getRemotePort());
        }
        return null;
    }

}

where blackList contains list of application IDs (Spring Boot application name) managed for example via some RESTful API.

Jolt answered 26/10, 2016 at 11:27 Comment(1)
I thought about a filter too, but I wanted to try to use the native properties of Zuul :)Unrefined
U
0

After a lot of efforts I came up with this solution. First, I used Netflix Archaius to watch a property file. Then I proceeded as follows:

public class ApplicationRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

public ApplicationRouteLocator(String servletPath, ZuulProperties properties) {
    super(servletPath, properties );
}


@Override
public void refresh() {
   doRefresh();
}
}

Made the doRefresh() method public by extending SimpleRouteLocator and calling its method in the overridden one of the interface RefreshableRouteLocator.

Then I redefined the bean RouteLocator with my custom implementation:

@Configuration
@EnableConfigurationProperties( { ZuulProperties.class } )
public class ZuulConfig {

public static ApplicationRouteLocator simpleRouteLocator;

@Autowired
private ZuulProperties zuulProperties;

@Autowired
private ServerProperties server;

@Bean
@Primary
public RouteLocator routeLocator() {
    logger.info( "zuulProperties are: {}", zuulProperties );
    simpleRouteLocator = new ApplicationRouteLocator( this.server.getServletPrefix(),
            this.zuulProperties );


    ConfigurationManager.getConfigInstance().addConfigurationListener( configurationListener );

    return simpleRouteLocator;
}


private ConfigurationListener configurationListener =
        new ConfigurationListener() {

            @Override
            public void configurationChanged( ConfigurationEvent ce ) {

                            // zuulProperties.getRoutes() do something
                            // zuulProperties.getIgnoredPatterns() do something
                            simpleRouteLocator.refresh();
                        }



                }

}

Every time a property in the file was modified an event was triggered and the ConfigurationEvent was able to deal with it (getPropertyName() and getPropertyValue() to extract data from the event). Since I also Autowired the ZuulProperties I was able to get access to it. With the right rule I could find whether the property of Zuul

zuul.ignoredPatterns

was modified changing its value in the ZuulProperties accordingly.

Unrefined answered 24/5, 2017 at 9:11 Comment(1)
Cannot resolve method 'getServletPrefix' in 'ServerProperties'Operculum
D
0

Here refresh context should work (as long as you are not adding a new routing rule or removing a currently existing one), if you are adding or removing routing rules, you have to add a new bean for ZuulProperties and mark it with @RefreshScope, @Primary.

You can autowire refreshEndpoint bean for example and apply refreshEndpoint.refresh() on the listener.

Marking a custom RouteLocator as primary will cause problems as zuul already has bean of same type marked as primary.

Drag answered 26/9, 2017 at 7:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.