What about dynamic routing for Spring Cloud Zuul?Integrated Nacos implementation is simple

Keywords: Java github Redis Zookeeper

1. Description

The core concept of gateway is routing configuration and routing rules. As the entrance of all requests traffic, dynamic routing is necessary to avoid rebooting in the real production environment in order to ensure high reliability and high availability. This paper mainly introduces the idea of implementation and uses Nacos as the data source.explain

 

2. Implementing Key Points

To achieve dynamic routing, you only need to focus on the following four points

  1. How the dynamically routed data is loaded when the gateway starts
  2. Static and dynamic routing are predicated on that, ps: Static routing refers to the write-dead routing configuration in the configuration file
  3. Listening for data source changes in dynamic routing
  4. How to notify zuul to refresh routes when data changes

 

3. Specific implementation

3.1. Data loading for dynamic routing

  • Overrides the locateRoutes method of the SimpleRouteLocator class, which loads routing configurations, and gets routing configurations in properties from the parent class, which can be extended to achieve the purpose of dynamically obtaining configurations
  • Here, static and dynamic routes coexist, and the implementation of dynamic route-first override with the same route id

The AbstractDynRouteLocator class can be viewed: AbstractDynRouteLocator.java

public abstract class AbstractDynRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {
    private ZuulProperties properties;

    public AbstractDynRouteLocator(String servletPath, ZuulProperties properties) {
        super(servletPath, properties);
        this.properties = properties;
    }

    /**
     * Refresh Route
     */
    @Override
    public void refresh() {
        doRefresh();
    }

    @Override
    protected Map<String, ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<>();
        // Loading static routing information from application.properties
        routesMap.putAll(super.locateRoutes());
        // Loading dynamic routing information from a data source
        routesMap.putAll(loadDynamicRoute());
        // Optimize the configuration
        LinkedHashMap<String, ZuulRoute> values = new LinkedHashMap<>();
        for (Map.Entry<String, ZuulRoute> entry : routesMap.entrySet()) {
            String path = entry.getKey();
            // Prepend with slash if not already present.
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            values.put(path, entry.getValue());
        }
        return values;
    }

    /**
     * Load routing configuration, implemented by subclasses
     */
    public abstract Map<String, ZuulRoute> loadDynamicRoute();
}

Since dynamically routed data can be routed in many ways, such as Nacos, Redis, Zookeeper, DB, and so on, an abstract class is defined here, and the loadDynamicRoute method is defined by a specific implementation class.

 

3.2. Nacos Routing Implementation Class

The complete code implementation of the NacosDynRouteLocator class can be viewed: NacosDynRouteLocator.java

3.2.1. Implementing the loadDynamicRoute method to obtain dynamic data

    @Override
    public Map<String, ZuulProperties.ZuulRoute> loadDynamicRoute() {
        Map<String, ZuulRoute> routes = new LinkedHashMap<>();
        if (zuulRouteEntities == null) {
            zuulRouteEntities = getNacosConfig();
        }
        for (ZuulRouteEntity result : zuulRouteEntities) {
            if (StrUtil.isBlank(result.getPath()) || !result.isEnabled()) {
                continue;
            }
            ZuulRoute zuulRoute = new ZuulRoute();
            BeanUtil.copyProperties(result, zuulRoute);
            routes.put(zuulRoute.getPath(), zuulRoute);
        }
        return routes;
    }
        
    private List<ZuulRouteEntity> getNacosConfig() {
        try {
            String content = nacosConfigProperties.configServiceInstance().getConfig(ZUUL_DATA_ID, ZUUL_GROUP_ID,5000);
            return getListByStr(content);
        } catch (NacosException e) {
            log.error("listenerNacos-error", e);
        }
        return new ArrayList<>(0);
    }

3.2.2. Increase NacosListener to listen for routing data changes

    private void addListener() {
        try {
            nacosConfigProperties.configServiceInstance().addListener(ZUUL_DATA_ID, ZUUL_GROUP_ID, new Listener() {
                @Override
                public Executor getExecutor() {
                    return null;
                }

                @Override
                public void receiveConfigInfo(String configInfo) {
                    //Assignment Routing Information
                    locator.setZuulRouteEntities(getListByStr(configInfo));
                    RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(locator);
                    publisher.publishEvent(routesRefreshedEvent);
                }
            });
        } catch (NacosException e) {
            log.error("nacos-addListener-error", e);
        }
    }

Notice that you don't need to refresh the route manually after the route data changes, just send a RoutesRefreshedEvent event to zuul, who has his own ZuulRefreshListener class to listen for events to help us refresh the route

 

3.3. Configuration class creates Bean for NacosDynRouteLocator

DynamicZuulRouteConfig can be viewed: NacosDynRouteLocator.java

@Configuration
@ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "enabled", havingValue = "true")
public class DynamicZuulRouteConfig {
    @Autowired
    private ZuulProperties zuulProperties;

    @Autowired
    private DispatcherServletPath dispatcherServletPath;

    /**
     * Nacos Implementation
     */
    @Configuration
    @ConditionalOnProperty(prefix = "zlt.gateway.dynamicRoute", name = "dataType", havingValue = "nacos", matchIfMissing = true)
    public class NacosZuulRoute {
        @Autowired
        private NacosConfigProperties nacosConfigProperties;

        @Autowired
        private ApplicationEventPublisher publisher;

        @Bean
        public NacosDynRouteLocator nacosDynRouteLocator() {
            return new NacosDynRouteLocator(nacosConfigProperties, publisher, dispatcherServletPath.getPrefix(), zuulProperties);
        }
    }
}

Custom configuration controls whether dynamic routing is enabled or not

 

3.4. Add Nacos routing configuration


Add a new configuration item:

  • Data Id: zuul-routes
  • Group: ZUUL_GATEWAY
  • Configuration:
[
    {
        "enabled":true,
        "id":"csdn",
        "path":"/csdn/**",
        "retryable":false,
        "stripPrefix":true,
        "url":"https://www.csdn.net/"
    }, {
        "enabled":true,
        "id":"github",
        "path":"/github/**",
        "retryable":false,
        "stripPrefix":true,
        "url":"http://github.com/"
    }
]

Add two routes

 

IV. Testing

  • Start gateway to view current routing information through/actuator/routes endpoint

You can see that the static route and the two route information configured in Nacos are displayed side by side

  • Modify Nacos configuration to turn off csdn routing

  • Refresh View Gateway Routing Information

csdn's routing is no longer visible and dynamic routing configuration changes are implemented

 
Recommended reading

 
Please scan my public number

Posted by MG-WebDesigns on Wed, 28 Aug 2019 20:07:33 -0700