springcloud zuul 网关 持久化 动态加载路由的思路分析

在springcloud 最新的版本已经有了自己的gateway组件    

目前世面上都是基于netflix 出品 zuul 的 gateway       一般我们在生产上 都希望能将路由动态化 持久化  做动态管理

基本设想思路  通过后台页面来管理路由   然后 刷新配置    本文将探索一下如何进行 zuul 路由的数据库持久化  动态化 建议从github上下载spring-cloud-netflix源码 

根据一些教程很容易知道  配置路由映射  通过

1zuul.routes.<key>.path=/foo/** 2zuul.routes.<key>.service-id= 服务实例名称 3zuul.routes.<key>.url=http://xxxoo

 来设置   由此我们可以找到一个 ZuulProperties 的zuul配置类 ,从中我们发现有个属性  

1 /** 2 * Map of route names to properties. 3 */ 4 private Map<String, ZuulRoute> routes = new LinkedHashMap<>();

从名字上看 知道是路由集合 , 而且有对应的set方法 ,经过对配置元数据json的解读  确认 这就 是 装载路由的容器     我们可以在 ZuulProperties 初始化的时候 将路由装载到容器中   那么 ZuulRoute 又是个什么玩意儿呢:

1public static class ZuulRoute { 2 3 /** 4 * 路由的唯一编号 同时也默认为 装载路由的容器的Key 用来标识映射的唯一性 重要. 5 */ 6 private String id; 7 8 /** 9 * 路由的规则 /foo/**. 10 */ 11 private String path; 12 13 /** 14 * 服务实例ID(如果有的话)来映射到此路由 你可以指定一个服务或者url 但是不能两者同时对于一个key来配置 15 * 16 */ 17 private String serviceId; 18 19 /** 20 * 就是上面提到的url 21 * 22 */ 23 private String url; 24 25 /** 26 * 路由前缀是否在转发开始前被删除 默认是删除 27 * 举个例子 你实例的实际调用是http://localhost:8002/user/info 28 * 如果你路由设置该实例对应的path 为 /api/v1/** 那么 通过路由调用 29 * http://ip:port/api/v1/user/info 30 * 当为true 转发到 http://localhost:8002/user/info 31 * 当为false 转发到 http://localhost:8002//api/v1user/info 32 */ 33 private boolean stripPrefix = true; 34 35 /** 36 * 是否支持重试如果支持的话 通常需要服务实例id 跟ribbon 37 * 38 */ 39 private Boolean retryable; 40 41 /** 42 * 不传递到下游请求的敏感标头列表。默认为“安全”的头集,通常包含用户凭证。如果下游服务是与代理相同的系统的一 43 * 部分,那么将它们从列表中删除就可以了,因此它们共享身份验证数据。如果在自己的域之外使用物理URL,那么通常来 44 * 说泄露用户凭证是一个坏主意 45 */ 46 private Set<String> sensitiveHeaders = new LinkedHashSet<>(); 47 /** 48 * 上述列表sensitiveHeaders 是否生效 默认不生效 49 */ 50 private boolean customSensitiveHeaders = false;

上面这些就是我们需要进行 持久化 的东西

你可以用你知道的持久化方式 来实现 当然 这些可以加入缓存来减少IO提高性能    这里只说一个思路具体自己可以实现

当持久化完成后 我们如何让网关来刷新这些配置呢  每一次的curd 能迅速生效呢

这个就要 路由加载的机制和原理   路由是由路由定位器来 从配置中 获取路由表   进行匹配的

1package org.springframework.cloud.netflix.zuul.filters; 2 3import java.util.Collection; 4import java.util.List; 5 6/** 7 * @author Dave Syer 8 */ 9public interface RouteLocator { 10 11 /** 12 * Ignored route paths (or patterns), if any. 13 */ 14 Collection<String> getIgnoredPaths(); 15 16 /** 17 * 获取路由表. 18 */ 19 List<Route> getRoutes(); 20 21 /** 22 * 将路径映射到具有完整元数据的实际路由. 23 */ 24 Route getMatchingRoute(String path); 25 26}

 实现有这么几个:

第一个 复合定位器 CompositeRouteLocator

1public class CompositeRouteLocator implements RefreshableRouteLocator { 2 private final Collection<? extends RouteLocator> routeLocators; 3 private ArrayList<RouteLocator> rl; 4 5 public CompositeRouteLocator(Collection<? extends RouteLocator> routeLocators) { 6 Assert.notNull(routeLocators, "'routeLocators' must not be null"); 7 rl = new ArrayList<>(routeLocators); 8 AnnotationAwareOrderComparator.sort(rl); 9 this.routeLocators = rl; 10 } 11 12 @Override 13 public Collection<String> getIgnoredPaths() { 14 List<String> ignoredPaths = new ArrayList<>(); 15 for (RouteLocator locator : routeLocators) { 16 ignoredPaths.addAll(locator.getIgnoredPaths()); 17 } 18 return ignoredPaths; 19 } 20 21 @Override 22 public List<Route> getRoutes() { 23 List<Route> route = new ArrayList<>(); 24 for (RouteLocator locator : routeLocators) { 25 route.addAll(locator.getRoutes()); 26 } 27 return route; 28 } 29 30 @Override 31 public Route getMatchingRoute(String path) { 32 for (RouteLocator locator : routeLocators) { 33 Route route = locator.getMatchingRoute(path); 34 if (route != null) { 35 return route; 36 } 37 } 38 return null; 39 } 40 41 @Override 42 public void refresh() { 43 for (RouteLocator locator : routeLocators) { 44 if (locator instanceof RefreshableRouteLocator) { 45 ((RefreshableRouteLocator) locator).refresh(); 46 } 47 } 48 } 49}

这是一个既可以刷新 又可以定位的定位器   作用 可以将一个到多个定位器转换成 可刷新的定位器

看构造   传入路由定位器集合  然后   进行了排序 赋值  同时实现了  路由定位器的方法   跟刷新方法

我们刷新 可以根据将定位器  放入这个容器进行转换   

第二个  DiscoveryClientRouteLocator    是组合  静态  以及配置好的路由 跟一个服务发现实例  而且有优先权

第三个 **RefreshableRouteLocator   **实现 即可实现  动态刷新逻辑

第四个 **Simple****RouteLocator **   可以发现 第二个 继承了此定位器  说明 这个是一个基础的实现   基于所有的配置

1public class SimpleRouteLocator implements RouteLocator, Ordered { 2 3 private static final Log log = LogFactory.getLog(SimpleRouteLocator.class); 4 5 private static final int DEFAULT_ORDER = 0; 6 7 private ZuulProperties properties; 8 9 private PathMatcher pathMatcher = new AntPathMatcher(); 10 11 private String dispatcherServletPath = "/"; 12 private String zuulServletPath; 13 14 private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>(); 15 private int order = DEFAULT_ORDER; 16 17 public SimpleRouteLocator(String servletPath, ZuulProperties properties) { 18 this.properties = properties; 19 if (StringUtils.hasText(servletPath)) { 20 this.dispatcherServletPath = servletPath; 21 } 22 23 this.zuulServletPath = properties.getServletPath(); 24 } 25 26 @Override 27 public List<Route> getRoutes() { 28 List<Route> values = new ArrayList<>(); 29 for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) { 30 ZuulRoute route = entry.getValue(); 31 String path = route.getPath(); 32 values.add(getRoute(route, path)); 33 } 34 return values; 35 } 36 37 @Override 38 public Collection<String> getIgnoredPaths() { 39 return this.properties.getIgnoredPatterns(); 40 } 41 42 @Override 43 public Route getMatchingRoute(final String path) { 44 45 return getSimpleMatchingRoute(path); 46 47 } 48 49 protected Map<String, ZuulRoute> getRoutesMap() { 50 if (this.routes.get() == null) { 51 this.routes.set(locateRoutes()); 52 } 53 return this.routes.get(); 54 } 55 56 protected Route getSimpleMatchingRoute(final String path) { 57 if (log.isDebugEnabled()) { 58 log.debug("Finding route for path: " + path); 59 } 60 61 // This is called for the initialization done in getRoutesMap() 62 getRoutesMap(); 63 64 if (log.isDebugEnabled()) { 65 log.debug("servletPath=" + this.dispatcherServletPath); 66 log.debug("zuulServletPath=" + this.zuulServletPath); 67 log.debug("RequestUtils.isDispatcherServletRequest()=" 68 + RequestUtils.isDispatcherServletRequest()); 69 log.debug("RequestUtils.isZuulServletRequest()=" 70 + RequestUtils.isZuulServletRequest()); 71 } 72 73 String adjustedPath = adjustPath(path); 74 75 ZuulRoute route = getZuulRoute(adjustedPath); 76 77 return getRoute(route, adjustedPath); 78 } 79 80 protected ZuulRoute getZuulRoute(String adjustedPath) { 81 if (!matchesIgnoredPatterns(adjustedPath)) { 82 for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) { 83 String pattern = entry.getKey(); 84 log.debug("Matching pattern:" + pattern); 85 if (this.pathMatcher.match(pattern, adjustedPath)) { 86 return entry.getValue(); 87 } 88 } 89 } 90 return null; 91 } 92 93 protected Route getRoute(ZuulRoute route, String path) { 94 if (route == null) { 95 return null; 96 } 97 if (log.isDebugEnabled()) { 98 log.debug("route matched=" + route); 99 } 100 String targetPath = path; 101 String prefix = this.properties.getPrefix(); 102 if (path.startsWith(prefix) && this.properties.isStripPrefix()) { 103 targetPath = path.substring(prefix.length()); 104 } 105 if (route.isStripPrefix()) { 106 int index = route.getPath().indexOf("*") - 1; 107 if (index > 0) { 108 String routePrefix = route.getPath().substring(0, index); 109 targetPath = targetPath.replaceFirst(routePrefix, ""); 110 prefix = prefix + routePrefix; 111 } 112 } 113 Boolean retryable = this.properties.getRetryable(); 114 if (route.getRetryable() != null) { 115 retryable = route.getRetryable(); 116 } 117 return new Route(route.getId(), targetPath, route.getLocation(), prefix, 118 retryable, 119 route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null, 120 route.isStripPrefix()); 121 } 122 123 /** 124 * Calculate all the routes and set up a cache for the values. Subclasses can call 125 * this method if they need to implement {@link RefreshableRouteLocator}. 126 */ 127 protected void doRefresh() { 128 this.routes.set(locateRoutes()); 129 } 130 131 /** 132 * Compute a map of path pattern to route. The default is just a static map from the 133 * {@link ZuulProperties}, but subclasses can add dynamic calculations. 134 */ 135 protected Map<String, ZuulRoute> locateRoutes() { 136 LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>(); 137 for (ZuulRoute route : this.properties.getRoutes().values()) { 138 routesMap.put(route.getPath(), route); 139 } 140 return routesMap; 141 } 142 143 protected boolean matchesIgnoredPatterns(String path) { 144 for (String pattern : this.properties.getIgnoredPatterns()) { 145 log.debug("Matching ignored pattern:" + pattern); 146 if (this.pathMatcher.match(pattern, path)) { 147 log.debug("Path " + path + " matches ignored pattern " + pattern); 148 return true; 149 } 150 } 151 return false; 152 } 153 154 private String adjustPath(final String path) { 155 String adjustedPath = path; 156 157 if (RequestUtils.isDispatcherServletRequest() 158 && StringUtils.hasText(this.dispatcherServletPath)) { 159 if (!this.dispatcherServletPath.equals("/")) { 160 adjustedPath = path.substring(this.dispatcherServletPath.length()); 161 log.debug("Stripped dispatcherServletPath"); 162 } 163 } 164 else if (RequestUtils.isZuulServletRequest()) { 165 if (StringUtils.hasText(this.zuulServletPath) 166 && !this.zuulServletPath.equals("/")) { 167 adjustedPath = path.substring(this.zuulServletPath.length()); 168 log.debug("Stripped zuulServletPath"); 169 } 170 } 171 else { 172 // do nothing 173 } 174 175 log.debug("adjustedPath=" + adjustedPath); 176 return adjustedPath; 177 } 178 179 @Override 180 public int getOrder() { 181 return order; 182 } 183 184 public void setOrder(int order) { 185 this.order = order; 186 } 187 188}

我们可以从   第一跟第四个下功夫

将配置从DB读取 放入 Simple****RouteLocator **    再注入到CompositeRouteLocator**

刷新的核心类 :

1package org.springframework.cloud.netflix.zuul; 2 3import org.springframework.cloud.netflix.zuul.filters.RouteLocator; 4import org.springframework.context.ApplicationEvent; 5 6/** 7 * @author Dave Syer 8 */ 9@SuppressWarnings("serial") 10public class RoutesRefreshedEvent extends ApplicationEvent { 11 12 private RouteLocator locator; 13 14 public RoutesRefreshedEvent(RouteLocator locator) { 15 super(locator); 16 this.locator = locator; 17 } 18 19 public RouteLocator getLocator() { 20 return this.locator; 21 } 22 23}

基于 事件    我们只要写一个监听器 来监听 就OK了  具体  自行实现 

这是我自己实现的  目前多实例情况下还不清楚是否会广播   如果不能广播 可参考config 刷新的思路来解决

1@Configuration 2public class ZuulConfig { 3 @Resource 4 private IRouterService routerService; 5 @Resource 6 private ServerProperties serverProperties; 7 8 /** 9 * 将数据库的网关配置数据 写入配置 10 * 11 * @return the zuul properties 12 */ 13 @Bean 14 public ZuulProperties zuulProperties() { 15 ZuulProperties zuulProperties = new ZuulProperties(); 16 zuulProperties.setRoutes(routerService.initRoutersFromDB()); 17 return zuulProperties; 18 } 19 20 21 /** 22 * 将配置写入可刷新的路由定位器. 23 * 24 * @param zuulProperties the zuul properties 25 * @return the composite route locator 26 */ 27 @Bean 28 @ConditionalOnBean(ZuulProperties.class) 29 public CompositeRouteLocator compositeRouteLocator(@Qualifier("zuulProperties") ZuulProperties zuulProperties) { 30 List<RouteLocator> routeLocators = new ArrayList<>(); 31 RouteLocator simpleRouteLocator = new SimpleRouteLocator(serverProperties.getServletPrefix(), zuulProperties); 32 routeLocators.add(simpleRouteLocator); 33 return new CompositeRouteLocator(routeLocators); 34 } 35 36}

路由刷新器:

1@Service 2public class ZuulRefresherImpl implements ZuulRefresher { 3 4 private static final Logger log = LoggerFactory.getLogger(ZuulRefresher.class); 5 @Resource 6 private ApplicationEventPublisher applicationEventPublisher; 7 @Resource 8 private IRouterService iRouterService; 9 @Resource 10 private ServerProperties serverProperties; 11 @Resource 12 private ZuulProperties zuulProperties; 13 @Resource 14 private CompositeRouteLocator compositeRouteLocator; 15 16 @Override 17 public void refreshRoutes() { 18 19 zuulProperties.setRoutes(iRouterService.initRoutersFromDB()); 20 21 List<RouteLocator> routeLocators = new ArrayList<>(); 22 RouteLocator simpleRouteLocator = new SimpleRouteLocator(serverProperties.getServletPrefix(), zuulProperties); 23 routeLocators.add(simpleRouteLocator); 24 25 compositeRouteLocator = new CompositeRouteLocator(routeLocators); 26 RoutesRefreshedEvent routesRefreshedEvent = new RoutesRefreshedEvent(compositeRouteLocator); 27 applicationEventPublisher.publishEvent(routesRefreshedEvent); 28 log.info("zuul 路由已刷新"); 29 } 30 31}

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Srping cloud gateway 实现动态路由(MySQL持久化+redis分布式缓存) 最新

摘要本文讲解在SpringCloud中如何通过MySQL和redis实现动态路由配置,以及路由信息持久化在MySQL中,同时使用Redis作为分布式路由信息缓存。无广告原文链接:Srpingcloudgateway实现动态路由(MySQL持久化redis分布式缓存)(https://www.oschina.net