zdm 6 years ago
parent
commit
99e4a9c169

+ 41 - 0
src/main/java/com/yihu/ehr/zuul/config/ComplexRouteConfig.java

@ -0,0 +1,41 @@
package com.yihu.ehr.zuul.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
/**
 * Created by progr1mmer on 2018/8/24.
 */
@Configuration
public class ComplexRouteConfig {
    @Value("${zuul.config-priority:false}")
    private Boolean configPriority;
    @Autowired
    private ZuulProperties zuulProperties;
    @Autowired
    private ServerProperties server;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Bean
    public JdbcZuulRouteService jdbcZuulRouteService(JdbcTemplate jdbcTemplate){
        return new JdbcZuulRouteService(jdbcTemplate);
    }
    @Bean
    public ComplexRouteLocator routeLocator() {
        return new ComplexRouteLocator(
                server.getServletPrefix(),
                zuulProperties,
                jdbcZuulRouteService(jdbcTemplate),
                configPriority);
    }
}

+ 82 - 0
src/main/java/com/yihu/ehr/zuul/config/ComplexRouteEndPoint.java

@ -0,0 +1,82 @@
package com.yihu.ehr.zuul.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
 * Created by progr1mmer on 2018/8/24.
 */
@RestController
@RequestMapping("/route")
public class ComplexRouteEndPoint {
    @Autowired
    private ComplexRouteLocator complexRouteLocator;
    @Autowired
    private JdbcZuulRouteService jdbcZuulRouteService;
    @PostMapping
    public String save (
            //主键
            @RequestParam(name = "id") String id,
            //网关暴露出去的路径   例:/v1/simple2/**"
            @RequestParam(name = "path") String path,
            //注册到微服务的Id
            @RequestParam(name = "serviceId", required = false) String serviceId,
            //跳转的路径   例:http://localhost:10010/
            @RequestParam(name = "url", required = false) String url,
            //失败是否重试
            @RequestParam(name = "retryable", required = false, defaultValue = "true") Boolean retryable) {
        ZuulProperties.ZuulRoute ruleRoute = new ZuulProperties.ZuulRoute();
        ruleRoute.setId(id);
        // Prepend with slash if not already present.
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        if (StringUtils.hasText(complexRouteLocator.getProperties().getPrefix())) {
            path = complexRouteLocator.getProperties().getPrefix() + path;
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
        }
        ruleRoute.setPath(path);
        ruleRoute.setServiceId(serviceId);
        ruleRoute.setRetryable(retryable);
        ruleRoute.setUrl(url);
        jdbcZuulRouteService.addZuulRoute(ruleRoute);
        complexRouteLocator.refresh();
        return "success";
    }
    @DeleteMapping
    public String remove (
            @RequestParam(name = "id") String id) {
        jdbcZuulRouteService.removeZuulRoute(id);
        complexRouteLocator.getProperties().getRoutes().remove(id);
        complexRouteLocator.refresh();
        return "success";
    }
    @PutMapping
    public String enable(
            @RequestParam(name = "id") String id,
            @RequestParam(name = "enabled") Integer enabled) {
        jdbcZuulRouteService.updateZuulRouteEnable(id, enabled);
        complexRouteLocator.getProperties().getRoutes().remove(id);
        complexRouteLocator.refresh();
        return "success";
    }
    @GetMapping
    public List<ZuulProperties.ZuulRoute> getRules() {
        List<ZuulProperties.ZuulRoute> zuulRoutes = new ArrayList<>();
        complexRouteLocator.getProperties().getRoutes().forEach((key, val) -> zuulRoutes.add(val));
        return zuulRoutes;
    }
}

+ 251 - 0
src/main/java/com/yihu/ehr/zuul/config/ComplexRouteLocator.java

@ -0,0 +1,251 @@
package com.yihu.ehr.zuul.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.netflix.zuul.filters.RefreshableRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.util.RequestUtils;
import org.springframework.core.Ordered;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
/**
 * Created by progr1mmer on 2018/8/24.
 */
public class ComplexRouteLocator implements RefreshableRouteLocator, Ordered {
    private static final Log log = LogFactory.getLog(SimpleRouteLocator.class);
    private static final int DEFAULT_ORDER = 0;
    private ZuulProperties properties;
    private PathMatcher pathMatcher = new AntPathMatcher();
    private String dispatcherServletPath = "/";
    private String zuulServletPath;
    private AtomicReference<Map<String, ZuulProperties.ZuulRoute>> routes = new AtomicReference<>();
    private int order = DEFAULT_ORDER;
    private final JdbcZuulRouteService jdbcZuulRouteService;
    private final boolean configPriority;
    public ComplexRouteLocator(
            String servletPath,
            ZuulProperties properties,
            JdbcZuulRouteService jdbcZuulRouteService,
            boolean configPriority) {
        this.properties = properties;
        if (StringUtils.hasText(servletPath)) {
            this.dispatcherServletPath = servletPath;
        }
        this.zuulServletPath = properties.getServletPath();
        this.jdbcZuulRouteService = jdbcZuulRouteService;
        this.configPriority = configPriority;
    }
    @Override
    public List<Route> getRoutes() {
        List<Route> values = new ArrayList<>();
        for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : getRoutesMap().entrySet()) {
            ZuulProperties.ZuulRoute route = entry.getValue();
            String path = route.getPath();
            values.add(getRoute(route, path));
        }
        return values;
    }
    @Override
    public Collection<String> getIgnoredPaths() {
        return this.properties.getIgnoredPatterns();
    }
    @Override
    public Route getMatchingRoute(final String path) {
        return getSimpleMatchingRoute(path);
    }
    protected Map<String, ZuulProperties.ZuulRoute> getRoutesMap() {
        if (this.routes.get() == null) {
            this.routes.set(locateRoutes());
        }
        return this.routes.get();
    }
    protected Route getSimpleMatchingRoute(final String path) {
        if (log.isDebugEnabled()) {
            log.debug("Finding route for path: " + path);
        }
        // This is called for the initialization done in getRoutesMap()
        getRoutesMap();
        if (log.isDebugEnabled()) {
            log.debug("servletPath=" + this.dispatcherServletPath);
            log.debug("zuulServletPath=" + this.zuulServletPath);
            log.debug("RequestUtils.isDispatcherServletRequest()="
                    + RequestUtils.isDispatcherServletRequest());
            log.debug("RequestUtils.isZuulServletRequest()="
                    + RequestUtils.isZuulServletRequest());
        }
        String adjustedPath = adjustPath(path);
        ZuulProperties.ZuulRoute route = getZuulRoute(adjustedPath);
        return getRoute(route, adjustedPath);
    }
    protected ZuulProperties.ZuulRoute getZuulRoute(String adjustedPath) {
        if (!matchesIgnoredPatterns(adjustedPath)) {
            for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : getRoutesMap().entrySet()) {
                String pattern = entry.getKey();
                log.debug("Matching pattern:" + pattern);
                if (this.pathMatcher.match(pattern, adjustedPath)) {
                    return entry.getValue();
                }
            }
        }
        return null;
    }
    protected Route getRoute(ZuulProperties.ZuulRoute route, String path) {
        if (route == null) {
            return null;
        }
        if (log.isDebugEnabled()) {
            log.debug("route matched=" + route);
        }
        String targetPath = path;
        String prefix = this.properties.getPrefix();
        if(prefix.endsWith("/")) {
            prefix = prefix.substring(0, prefix.length() - 1);
        }
        if (path.startsWith(prefix + "/") && this.properties.isStripPrefix()) {
            targetPath = path.substring(prefix.length());
        }
        if (route.isStripPrefix()) {
            int index = route.getPath().indexOf("*") - 1;
            if (index > 0) {
                String routePrefix = route.getPath().substring(0, index);
                targetPath = targetPath.replaceFirst(routePrefix, "");
                prefix = prefix + routePrefix;
            }
        }
        Boolean retryable = this.properties.getRetryable();
        if (route.getRetryable() != null) {
            retryable = route.getRetryable();
        }
        return new Route(route.getId(), targetPath, route.getLocation(), prefix,
                retryable,
                route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null,
                route.isStripPrefix());
    }
    /**
     * Calculate all the routes and set up a cache for the values. Subclasses can call
     * this method if they need to implement {@link RefreshableRouteLocator}.
     */
    protected void doRefresh() {
        this.routes.set(locateRoutes());
    }
    /**
     * Compute a map of path pattern to route. The default is just a static map from the
     * {@link ZuulProperties}, but subclasses can add dynamic calculations.
     */
    protected Map<String, ZuulProperties.ZuulRoute> locateRoutes() {
        LinkedHashMap<String, ZuulProperties.ZuulRoute> routesMap = new LinkedHashMap<>();
        List<ZuulProperties.ZuulRoute> zuulRoutes = jdbcZuulRouteService.loadEnabledZuulRoutes();
        zuulRoutes.forEach(item -> {
            if (configPriority) {
                if (properties.getRoutes().containsKey(item.getId())) {
                    return;
                }
            }
            String path = item.getPath();
            if (!path.startsWith("/")) {
                path = "/" + path;
            }
            if (StringUtils.hasText(this.properties.getPrefix())) {
                path = this.properties.getPrefix() + path;
                if (!path.startsWith("/")) {
                    path = "/" + path;
                }
            }
            item.setPath(path);
            this.properties.getRoutes().put(item.getId(), item);
        });
        for (ZuulProperties.ZuulRoute route : this.properties.getRoutes().values()) {
            routesMap.put(route.getPath(), route);
        }
        return routesMap;
    }
    protected boolean matchesIgnoredPatterns(String path) {
        for (String pattern : this.properties.getIgnoredPatterns()) {
            log.debug("Matching ignored pattern:" + pattern);
            if (this.pathMatcher.match(pattern, path)) {
                log.debug("Path " + path + " matches ignored pattern " + pattern);
                return true;
            }
        }
        return false;
    }
    private String adjustPath(final String path) {
        String adjustedPath = path;
        if (RequestUtils.isDispatcherServletRequest()
                && StringUtils.hasText(this.dispatcherServletPath)) {
            if (!this.dispatcherServletPath.equals("/")) {
                adjustedPath = path.substring(this.dispatcherServletPath.length());
                log.debug("Stripped dispatcherServletPath");
            }
        }
        else if (RequestUtils.isZuulServletRequest()) {
            if (StringUtils.hasText(this.zuulServletPath)
                    && !this.zuulServletPath.equals("/")) {
                adjustedPath = path.substring(this.zuulServletPath.length());
                log.debug("Stripped zuulServletPath");
            }
        }
        else {
            // do nothing
        }
        log.debug("adjustedPath=" + adjustedPath);
        return adjustedPath;
    }
    @Override
    public int getOrder() {
        return order;
    }
    public void setOrder(int order) {
        this.order = order;
    }
    @Override
    public void refresh() {
        doRefresh();
    }
    public ZuulProperties getProperties() {
        return properties;
    }
}

+ 55 - 0
src/main/java/com/yihu/ehr/zuul/config/JdbcZuulRouteService.java

@ -0,0 +1,55 @@
package com.yihu.ehr.zuul.config;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
/**
 * Created by progr1mmer on 2018/8/24.
 */
public class JdbcZuulRouteService {
    private final JdbcTemplate jdbcTemplate;
    public JdbcZuulRouteService(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate = jdbcTemplate;
    }
    public void addZuulRoute(ZuulProperties.ZuulRoute zuulRoute) {
        jdbcTemplate.update("insert into zuul_route (id, path, service_id, url, "
                + "retryable, enabled) values (?, ?, ?, ?, ?, ?)", getFieldsForUpdate(zuulRoute));
    }
    public int removeZuulRoute(String id) {
        return jdbcTemplate.update("delete from zuul_route where id = ?", id);
    }
    public int updateZuulRouteEnable(String id, int enabled) {
        return jdbcTemplate.update("update zuul_route set enabled = ? where id = ?", new Object[]{enabled, id});
    }
    public List<ZuulProperties.ZuulRoute> loadZuulRoutes() {
        List<ZuulProperties.ZuulRoute> zuulRoutes = jdbcTemplate.query("select id, path, service_id, url, "
                + "retryable, enabled from zuul_route", new BeanPropertyRowMapper<>(ZuulProperties.ZuulRoute.class));
        return zuulRoutes;
    }
    public List<ZuulProperties.ZuulRoute> loadEnabledZuulRoutes() {
        List<ZuulProperties.ZuulRoute> zuulRoutes = jdbcTemplate.query("select id, path, service_id, url, "
                + "retryable, enabled from zuul_route where enabled = 1", new BeanPropertyRowMapper<>(ZuulProperties.ZuulRoute.class));
        return zuulRoutes;
    }
    private Object [] getFieldsForUpdate(ZuulProperties.ZuulRoute zuulRoute) {
        return new Object[]{
                zuulRoute.getId(),
                zuulRoute.getPath(),
                zuulRoute.getServiceId(),
                zuulRoute.getUrl(),
                zuulRoute.getRetryable(),
                1
        };
    }
}

+ 1 - 0
src/main/resources/META-INF/app.properties

@ -0,0 +1 @@
app.name=ag-zuul

+ 12 - 0
src/main/resources/application.yml

@ -9,6 +9,13 @@ info:
  component: Zuul Server
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    max-active: 5
    max-idle: 2
    min-idle: 2
    validation-query: SELECT 1
    test-on-borrow: true
  redis:
    database: 0
    timeout: 0
@ -19,6 +26,7 @@ spring:
      min-idle: 1
zuul:
  config-priority: false
  ignored-services: '*'
  sensitive-headers:
  routes:
@ -62,6 +70,10 @@ zuul:
---
spring:
  profiles: dev
  datasource:
    url: jdbc:mysql://172.19.103.50:3306/healtharchive?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: chenweishan
    password: chenweishan
  redis:
    host: 172.19.103.47
    port: 6379