Spring Cloud gateway service zuul three dynamic routing

Keywords: Programming Spring Database Java JDBC

zuul dynamic routing

Gateway service is the only access to traffic. Don't stop the service. So dynamic routing is particularly necessary.

Database dynamic routing is based on event refresh mechanism to modify zuul's routing properties.

DiscoveryClientRouteLocator

You can see that DiscoveryClientRouteLocator is the core processing class of the default refresh.


//Reload the protected method. A sub method re method is required.
protected LinkedHashMap<String, ZuulRoute> locateRoutes() 

//Refresh triggered method RefreshableRouteLocator interface
 public void refresh() {
			this.doRefresh();
	}

Both of these methods inherit the SimpleRouteLocator class and operate again. In fact, the official method notes. If you need to read the load mapping dynamically. You need subclasses to override these two methods. Carry out specific implementation

First, the import of pom jar package needs to connect to mysql database.

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- jdbc -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

Route entity

package com.xian.cloud.entity;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * <Description> Routing entity class
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/10/30 15:00
 */
@Data
public class ZuulRouteEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * router Id
     */
    private Integer id;
    /**
     * Routing path
     */
    private String path;
    /**
     * Service name
     */
    private String serviceId;
    /**
     * url agent
     */
    private String url;
    /**
     * Forward without prefix
     */
    private String stripPrefix;
    /**
     * Do you want to retry?
     */
    private String retryable;
    /**
     * Is it enabled?
     */
    private String enabled;
    /**
     * Sensitive request header
     */
    private String sensitiveheadersList;
    /**
     * Creation time
     */
    private Date createTime;
    /**
     * Update time
     */
    private Date updateTime;
    /**
     * Delete ID (0-normal, 1-delete)
     */
    private String delFlag;
}

The new DiscoveryRouteLocator class parent class interface does not change


package com.xian.cloud.router;


import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import com.xian.cloud.entity.ZuulRoute;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * <Description>
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/10/30 18:57
 */
@Slf4j
public class DiscoveryRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator {

    private ZuulProperties properties;

    private JdbcTemplate jdbcTemplate;

    public DiscoveryRouteLocator(String servletPath, ZuulProperties properties, JdbcTemplate jdbcTemplate) {
        super(servletPath, properties);
        this.properties = properties;
        this.jdbcTemplate = jdbcTemplate;
        log.info("servletPath:{}",servletPath);
    }

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

    @Override
    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<String, ZuulProperties.ZuulRoute>();
        //Load route information from configuration file
        routesMap.putAll(super.locateRoutes());
        //Custom load route information
        routesMap.putAll(getRouteList());
        //Optimize the configuration
        LinkedHashMap<String, ZuulProperties.ZuulRoute> values = new LinkedHashMap<>();
        for (Map.Entry<String, ZuulProperties.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;
    }

    /**
     * Read zuul routing rules from database
     * @return
     */
   private LinkedHashMap<String, ZuulProperties.ZuulRoute> getRouteList() {
        LinkedHashMap<String, ZuulProperties.ZuulRoute> zuulRoutes = new LinkedHashMap<>();
        List<ZuulRoute> sysZuulRoutes = jdbcTemplate.query("select * from sys_zuul_route where del_flag = 0", new BeanPropertyRowMapper<>(ZuulRoute.class));

       for (ZuulRoute route: sysZuulRoutes) {

           // Skip over
           if (Strings.isNullOrEmpty(route.getPath()) && Strings.isNullOrEmpty(route.getUrl())) {
               continue;
           }

           ZuulProperties.ZuulRoute zuulRoute = new ZuulProperties.ZuulRoute();
           try {
               zuulRoute.setId(route.getServiceId());
               zuulRoute.setPath(route.getPath());
               zuulRoute.setServiceId(route.getServiceId());
               zuulRoute.setRetryable(Objects.equals("0", route.getRetryable()) ? Boolean.FALSE : Boolean.TRUE);
               zuulRoute.setStripPrefix(Objects.equals("0", route.getStripPrefix()) ? Boolean.FALSE : Boolean.TRUE);
               zuulRoute.setUrl(route.getUrl());
               List<String> sensitiveHeadersList = Arrays.asList(route.getSensitiveheadersList().split(","));
               if (sensitiveHeadersList != null) {
                   Set<String> sensitiveHeaderSet = Sets.newHashSet();
                   sensitiveHeadersList.forEach(sensitiveHeader -> sensitiveHeaderSet.add(sensitiveHeader));
                   zuulRoute.setSensitiveHeaders(sensitiveHeaderSet);
                   zuulRoute.setCustomSensitiveHeaders(true);
               }
           } catch (Exception e) {
               log.error("Database load configuration exception", e);
           }
           log.info("Customized routing configuration,path: {},serviceId:{}", zuulRoute.getPath(), zuulRoute.getServiceId());
           zuulRoutes.put(zuulRoute.getPath(), zuulRoute);

       }
        return zuulRoutes;
   }
}

We also need a producer and consumer diagram of events to easily integrate into one class

package com.xian.cloud.event;

import com.xian.cloud.router.DiscoveryRouteLocator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.client.discovery.event.HeartbeatMonitor;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;

/**
 * <Description> Route refresh event publishing and event listener
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/10/30 15:27
 */
@Service
public class RefreshRouteService implements ApplicationListener<ApplicationEvent> {

    @Autowired
    private ZuulHandlerMapping zuulHandlerMapping;

    private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

    @Autowired
    ApplicationEventPublisher publisher;

    @Autowired
    private DiscoveryRouteLocator dynamicRouteLocator;

    /**
     * Dynamic routing implementation calls refreshRoute() to publish refresh route events
     */
    public void refreshRoute() {
        RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(dynamicRouteLocator);
        publisher.publishEvent(routesRefreshedEvent);
    }

    /**
     * Event listener. Monitoring and detection event refresh
     * @param event
     */
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof ContextRefreshedEvent || event instanceof RefreshScopeRefreshedEvent || event instanceof RoutesRefreshedEvent){
            //Active manual refresh. Context refresh, configuration property refresh
            zuulHandlerMapping.setDirty(true);
        }else if(event instanceof HeartbeatEvent){
            //Heartbeat triggered, local mapping relationship. Relate to remote service
            HeartbeatEvent heartbeatEvent = (HeartbeatEvent)event;
            if(heartbeatMonitor.update(heartbeatEvent.getValue())){
                zuulHandlerMapping.setDirty(true);
            }
        }
    }
}

Provide external trigger interface

package com.xian.cloud.controller;

import com.xian.cloud.event.RefreshRouteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <Description> Refresh external interface manually
 *
 * @author xianliru@100tal.com
 * @version 1.0
 * @createDate 2019/10/30 20:23
 */
@RestController
public class RefreshController {

    @Autowired
    private RefreshRouteService refreshRouteService;

    @GetMapping("/refresh")
    public String refresh() {
        refreshRouteService.refreshRoute();
        return "refresh";
    }

}

Database table structure

CREATE TABLE `sys_zuul_route` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'router Id',
  `path` varchar(255) NOT NULL COMMENT 'Routing path',
  `service_id` varchar(255) NOT NULL COMMENT 'Service name',
  `url` varchar(255) DEFAULT NULL COMMENT 'url agent',
  `strip_prefix` char(1) DEFAULT '1' COMMENT 'Forward without prefix',
  `retryable` char(1) DEFAULT '1' COMMENT 'Do you want to retry?',
  `enabled` char(1) DEFAULT '1' COMMENT 'Is it enabled?',
  `sensitiveHeaders_list` varchar(255) DEFAULT NULL COMMENT 'Sensitive request header',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation time',
  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT 'Update time',
  `del_flag` char(1) DEFAULT '0' COMMENT 'Remove identity (0-normal,1-Delete)',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='Dynamic routing configuration table'

Comment out the configuration file client consumer service routing configuration. Set up the data source. Read from database

Start service print log

2019-10-30 20:49:39.946 info 63449 -- [taskscheduler-1] c.xian.cloud.router.dynamicproutelocator: add database customized routing configuration, path: / client / * *, serviceid: cloud discovery client
 2019-10-30 20:49:40.397 info 63449 -- [taskscheduler-1] c.xian.cloud.router.dynamicproutelocator: add database customized routing configuration, path: / client / * *, serviceid: cloud discovery client

postman requests the client interface to see if it can be forwarded successfully

Based on zuul dynamic gateway routing.

In the future, the gray scheme of the gateway and the debugging source service integrated by swagger2 will be updated. Coming soon!

From reference Official document of spring cloud

Reference books redefining spring cloud practice

Sample code address

Server nacos address http://47.99.209.72:8848/nacos

Previous address spring cloud alibaba address

Introduction to spring cloud alibaba

Spring Cloud Alibaba (nacos registry building)

Spring Cloud Alibaba uses nacos registry

Spring Cloud Alibaba nacos configuration center

spring cloud gateway service

Spring Cloud zuul gateway service I

Spring Cloud gateway service zuul II

How to like can pay attention to share this public number.

Copyright notice: This is the original article of the blogger, following CC 4.0 BY-SA copyright agreement. Please attach the original source link and this notice for reprint. Please upload the public number two-dimensional code.

Posted by Skepsis on Wed, 30 Oct 2019 17:58:38 -0700