Spring Cloud Gateway性能优化(巧妙优化RoutePredicateHandlerMapping)

先说一下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);
  }

}

很简单的方式 优化了这一部分的性能,而且非常灵活

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值