先说一下SpringCloud版本,引入的pom
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2022.0.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2022.0.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore-nio</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
RoutePredicateHandlerMapping的代码
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
....
// 慢的原因主要是 this.routeLocator.getRoutes() 当路由的数量非常多的时候,遍历很慢
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
return this.routeLocator.getRoutes()
// individually filter routes so that filterWhen error delaying is not a
// problem
.concatMap(route -> Mono.just(route).filterWhen(r -> {
// add the current route we are testing
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return r.getPredicate().apply(exchange);
})
// instead of immediately stopping main flux due to error, log and
// swallow it
.doOnError(e -> logger.error("Error applying predicate for route: " + route.getId(), e))
.onErrorResume(e -> Mono.empty()))
// .defaultIfEmpty() put a static Route not found
// or .switchIfEmpty()
// .switchIfEmpty(Mono.<Route>empty().log("noroute"))
.next()
// TODO: error handling
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange);
return route;
});
/*
* TODO: trace logging if (logger.isTraceEnabled()) {
* logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
*/
}
}
网上的方案,也查了很多,大家可以去查一下,都比较复杂!
下面说一下我的方案是在配置路由的时候可以配置metadata属性,类似如下
在metadata里面自定义配置 prefixPath,method(可选)
重写 RoutePredicateHandlerMapping 类并注册成spring的bean,代码如下
import org.springframework.cloud.gateway.config.GlobalCorsProperties;
import org.springframework.cloud.gateway.handler.FilteringWebHandler;
import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR;
/**
* CachingRoutePredicateHandlerMapping.
*
* @author dave
* @since 1.0
*/
@Component
public class MetadataRoutePredicateHandlerMapping extends RoutePredicateHandlerMapping {
private final RouteLocator routeLocatorCopy;
/**
* A copy of routing information that is specifically served for custom routing
* resolution. key-route definition id, value-route.
*/
private final Map<String, Route> routeCopy = new ConcurrentHashMap<>();
// RoutePredicateCacheable routePredicateCacheable
public MetadataRoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator,
GlobalCorsProperties globalCorsProperties, Environment environment) {
super(webHandler, routeLocator, globalCorsProperties, environment);
this.routeLocatorCopy = routeLocator;
subscribeRoute();
}
private void subscribeRoute() {
routeLocatorCopy.getRoutes().subscribe(r -> {
final Map<String, Object> metadata = r.getMetadata();
if (metadata.containsKey("prefixPath")) {
routeCopy.put(normalizePath(metadata.get("prefixPath").toString()), r);
}
});
}
private String normalizePath(String path) {
return Paths.get(path).normalize().toString();
}
@Override
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
final String method = exchange.getRequest().getMethod().name();
final String paths = exchange.getRequest().getPath().value();
final Route route = matchPath(method, paths);
if (route != null) {
// add the current route we are testing
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, route.getId());
return Mono.just(route);
}
return super.lookupRoute(exchange);
}
private Route matchPath(String method, String path) {
String newPath = String.copyValueOf(path.toCharArray());
for (;;) {
if (routeCopy.containsKey(newPath)) {
final Route r = routeCopy.get(newPath);
if (validateRoute(method, r)) {
return r;
}
}
final int index = newPath.lastIndexOf('/');
if (index > 0) {
newPath = path.substring(0, index);
continue;
}
if (index == 0) {
final Route r0 = routeCopy.getOrDefault("/", null);
if (r0 != null && validateRoute(method, r0)) {
return r0;
}
}
break;
}
return null;
}
private boolean validateRoute(String method, Route route) {
final Map<String, Object> metadata = route.getMetadata();
return !metadata.containsKey("method") || metadata.get("method").toString().equalsIgnoreCase(method);
}
}
很简单的方式 优化了这一部分的性能,而且非常灵活