Spring Cloud Edgware SR3 让Zuul支持形如 /xxx和/xxx/yyy 格式的路径配置
在包路径:org.springframework.cloud.netflix.zuul.filters 下,新建类SimpleRouteLocator,取代jar包中的类。内容如下:
1 /* 2 * Copyright 2013-2014 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.springframework.cloud.netflix.zuul.filters; 18 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.LinkedHashMap; 22 import java.util.List; 23 import java.util.Map; 24 import java.util.Map.Entry; 25 import java.util.concurrent.atomic.AtomicReference; 26 import org.apache.commons.logging.Log; 27 import org.apache.commons.logging.LogFactory; 28 import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute; 29 import org.springframework.cloud.netflix.zuul.util.RequestUtils; 30 import org.springframework.core.Ordered; 31 import org.springframework.util.AntPathMatcher; 32 import org.springframework.util.PathMatcher; 33 import org.springframework.util.StringUtils; 34 35 /** 36 * Simple {@link RouteLocator} based on configuration data held in {@link ZuulProperties}. 37 * 38 * @author Dave Syer 39 */ 40 public class SimpleRouteLocator implements RouteLocator, Ordered { 41 42 private static final Log log = LogFactory.getLog(SimpleRouteLocator.class); 43 44 private static final int DEFAULT_ORDER = 0; 45 46 private ZuulProperties properties; 47 48 private PathMatcher pathMatcher = new AntPathMatcher(); 49 50 private String dispatcherServletPath = "/"; 51 private String zuulServletPath; 52 53 private AtomicReference<Map<String, ZuulRoute>> routes = new AtomicReference<>(); 54 private int order = DEFAULT_ORDER; 55 56 public SimpleRouteLocator(String servletPath, ZuulProperties properties) { 57 this.properties = properties; 58 if (StringUtils.hasText(servletPath)) { 59 this.dispatcherServletPath = servletPath; 60 } 61 62 this.zuulServletPath = properties.getServletPath(); 63 } 64 65 @Override 66 public List<Route> getRoutes() { 67 List<Route> values = new ArrayList<>(); 68 for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) { 69 ZuulRoute route = entry.getValue(); 70 String path = route.getPath(); 71 values.add(getRoute(route, path)); 72 } 73 return values; 74 } 75 76 @Override 77 public Collection<String> getIgnoredPaths() { 78 return this.properties.getIgnoredPatterns(); 79 } 80 81 @Override 82 public Route getMatchingRoute(final String path) { 83 84 return getSimpleMatchingRoute(path); 85 86 } 87 88 protected Map<String, ZuulRoute> getRoutesMap() { 89 if (this.routes.get() == null) { 90 this.routes.set(locateRoutes()); 91 } 92 return this.routes.get(); 93 } 94 95 protected Route getSimpleMatchingRoute(final String path) { 96 if (log.isDebugEnabled()) { 97 log.debug("Finding route for path: " + path); 98 } 99 100 // This is called for the initialization done in getRoutesMap() 101 getRoutesMap(); 102 103 if (log.isDebugEnabled()) { 104 log.debug("servletPath=" + this.dispatcherServletPath); 105 log.debug("zuulServletPath=" + this.zuulServletPath); 106 log.debug("RequestUtils.isDispatcherServletRequest()=" 107 + RequestUtils.isDispatcherServletRequest()); 108 log.debug("RequestUtils.isZuulServletRequest()=" 109 + RequestUtils.isZuulServletRequest()); 110 } 111 112 String adjustedPath = adjustPath(path); 113 114 ZuulRoute route = getZuulRoute(adjustedPath); 115 116 return getRoute(route, adjustedPath); 117 } 118 119 protected ZuulRoute getZuulRoute(String adjustedPath) { 120 if (!matchesIgnoredPatterns(adjustedPath)) { 121 //2018年6月20日 马赛克:支持优先精确匹配-----start 122 Map m = (Map)this.routes.get(); 123 ZuulRoute z = (ZuulRoute)m.get(adjustedPath); 124 if(z!=null){ 125 return z; 126 } 127 //2018年6月20日 马赛克:支持优先精确匹配-----end 128 129 for (Entry<String, ZuulRoute> entry : getRoutesMap().entrySet()) { 130 String pattern = entry.getKey(); 131 log.debug("Matching pattern:" + pattern); 132 if (this.pathMatcher.match(pattern, adjustedPath)) { 133 return entry.getValue(); 134 } 135 } 136 } 137 return null; 138 } 139 140 protected Route getRoute(ZuulRoute route, String path) { 141 if (route == null) { 142 return null; 143 } 144 if (log.isDebugEnabled()) { 145 log.debug("route matched=" + route); 146 } 147 String targetPath = path; 148 String prefix = this.properties.getPrefix(); 149 if(prefix.endsWith("/")) { 150 prefix = prefix.substring(0, prefix.length() - 1); 151 } 152 if (path.startsWith(prefix + "/") && this.properties.isStripPrefix()) { 153 targetPath = path.substring(prefix.length()); 154 } 155 156 if (route.isStripPrefix()) { 157 int index = route.getPath().indexOf("*") - 1; 158 if (index > 0) { 159 String routePrefix = route.getPath().substring(0, index); 160 targetPath = targetPath.replaceFirst(routePrefix, ""); 161 prefix = prefix + routePrefix; 162 } 163 164 //2018年6月20日 马赛克:支持形如/xxx/bbb 而非/xxx/**的routes-----start 165 else{ 166 167 String routePath = route.getPath(); 168 targetPath = routePath.substring(routePath.lastIndexOf("/")); 169 prefix = prefix + routePath.substring(0,routePath.lastIndexOf("/")); 170 } 171 //2018年6月20日 马赛克:支持形如/xxx/bbb 而非/xxx/**的routes-----end 172 } 173 Boolean retryable = this.properties.getRetryable(); 174 if (route.getRetryable() != null) { 175 retryable = route.getRetryable(); 176 } 177 return new Route(route.getId(), targetPath, route.getLocation(), prefix, 178 retryable, 179 route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null, 180 route.isStripPrefix()); 181 } 182 183 /** 184 * Calculate all the routes and set up a cache for the values. Subclasses can call 185 * this method if they need to implement {@link RefreshableRouteLocator}. 186 */ 187 protected void doRefresh() { 188 this.routes.set(locateRoutes()); 189 } 190 191 /** 192 * Compute a map of path pattern to route. The default is just a static map from the 193 * {@link ZuulProperties}, but subclasses can add dynamic calculations. 194 */ 195 protected Map<String, ZuulRoute> locateRoutes() { 196 LinkedHashMap<String, ZuulRoute> routesMap = new LinkedHashMap<String, ZuulRoute>(); 197 for (ZuulRoute route : this.properties.getRoutes().values()) { 198 routesMap.put(route.getPath(), route); 199 } 200 return routesMap; 201 } 202 203 protected boolean matchesIgnoredPatterns(String path) { 204 for (String pattern : this.properties.getIgnoredPatterns()) { 205 log.debug("Matching ignored pattern:" + pattern); 206 if (this.pathMatcher.match(pattern, path)) { 207 log.debug("Path " + path + " matches ignored pattern " + pattern); 208 return true; 209 } 210 } 211 return false; 212 } 213 214 private String adjustPath(final String path) { 215 String adjustedPath = path; 216 217 if (RequestUtils.isDispatcherServletRequest() 218 && StringUtils.hasText(this.dispatcherServletPath)) { 219 if (!this.dispatcherServletPath.equals("/")) { 220 adjustedPath = path.substring(this.dispatcherServletPath.length()); 221 log.debug("Stripped dispatcherServletPath"); 222 } 223 } 224 else if (RequestUtils.isZuulServletRequest()) { 225 if (StringUtils.hasText(this.zuulServletPath) 226 && !this.zuulServletPath.equals("/")) { 227 adjustedPath = path.substring(this.zuulServletPath.length()); 228 log.debug("Stripped zuulServletPath"); 229 } 230 } 231 else { 232 // do nothing 233 } 234 235 log.debug("adjustedPath=" + adjustedPath); 236 return adjustedPath; 237 } 238 239 @Override 240 public int getOrder() { 241 return order; 242 } 243 244 public void setOrder(int order) { 245 this.order = order; 246 } 247 248 }
其中的164-171行,让zuul支持了如下配置:
zuul: routes: all: path: /vod2/two serviceId: all one: path: /vod1/one serviceId: one
通过/vod2/two路径,可以访问到 /all/two
注意,测试时不存在名为vod2和vod1的服务