SpringCloud Gateway 路由转发性能优化
对源码做简单修改,比如,Path 匹配 /mock/** 则对路由查找结果进行缓存(注意这里缓存策略和方式仅仅是举例,根据实际需求情况来做)
public static final String MOCK_PATCH = "/mock/**";private Map<String, Route> hashCache = new ConcurrentHashMap<>(1024);protected Mono<Route> lookupRoute(ServerWebExchange exchange) { String path = exchange.getRequest().getPath().subPath(0).value(); //符合Path规则,优先从缓存Map获取,时间复杂度近似于O(1) if (pathMatcher.match(MOCK_PATCH, path)) { return Mono.justOrEmpty(hashCache.get(path)) .switchIfEmpty(getRouteMono(exchange, path));} return getRouteMono(exchange, path);}private Mono<Route> getRouteMono(ServerWebExchange exchange, String path) { return this.routeLocator.getRoutes() //... 略过 .map(route -> { if (logger.isDebugEnabled()) { logger.debug("Route matched: " + route.getId()); } validateRoute(route, exchange); //符合Path规则,缓存路由 if (pathMatcher.match(MOCK_PATCH, path)) { hashCache.put(path, route); } return route; });}
继续查阅源码,找到 是如何装配的。在 中实现了 SpringCloud Gateway 内部组件的自动装配, 也在其中,代码入下:
@Beanpublic RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);}
很遗憾,官方没有给这个自动装配添加条件,我们无法自行装配替代默认装配。
我们只能采取以下步骤:
- 在 Springboot 启动类上增加排除 的自动装配配置;
- 继承 并完全拷贝其装配条件;
- 覆盖父类 routePredicateHandlerMapping方法,给装配添加条件;
- 继承 ,覆盖其 lookupRoute方法,符合一定条件的请求,优先从缓存中查找路由。
改造 Gateway
修改启动类,排除自动装配
@SpringBootApplication(exclude = GatewayConfiguration.class)public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args);}}
继承 GatewayAutoConfiguration
@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)@EnableConfigurationProperties@AutoConfigureBefore({HttpHandlerAutoConfiguration.class, WebFluxAutoConfiguration.class})@AutoConfigureAfter({GatewayLoadBalancerClientAutoConfiguration.class, GatewayClassPathWarningAutoConfiguration.class})@ConditionalOnClass(DispatcherHandler.class)public class CustomGatewayAutoConfiguration extends GatewayAutoConfiguration { // 实现自定义的RoutePredicateHandlerMapping装配 @Bean public CustomRoutePredicateHandlerMapping customRoutePredicateHandlerMapping( // 通过@Qualifier 制定装配的缓存管理器 @Qualifier("routeCacheManager") CacheManager routeCacheManager, FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { return new CustomRoutePredicateHandlerMapping( cacheManager, webHandler, routeLocator, globalCorsProperties, environment);} // 覆盖父类同名方法,增加使之失效的条件 @Bean @ConditionalOnMissingBean(RoutePredicateHandlerMapping.class) public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties, environment);}}
继承 RoutePredicateHandlerMapping
public class CustomRoutePredicateHandlerMapping extends RoutePredicateHandlerMapping { private final Cache specialCache; public CustomRoutePredicateHandlerMapping( CacheManager cacheManager, FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) { super(webHandler, routeLocator, globalCorsProperties, environment); specialCache = cacheManager.getCache("specialRouteCache");} @Override protected Mono<Route> lookupRoute(ServerWebExchange exchange) { //1. 从exchange中获取请求特征,如path //2. 如果符合特征 则使用缓存,从缓存获取,如果缓存未命中, // 调用 super.lookupRoute(exchange) 并加入缓存 //3. 不符合特征的,直接调用 // 下面演示使用 caffeine 缓存的方式 String specialPath = exchange.getRequest().getPath().subPath(0).value(); // 判断path是否符合缓存规则(一般而言用于仅采用Path断言,或简单结合header或query的情况,下面以只有path为例) if (checkPath(specialPath)) { return CacheMono // 查找缓存 .lookup( key -> Mono.justOrEmpty(specialCache.get(key, Route.class)).map(Signal::next), toKey(specialPath)) // 未命中直接查找路由表 .onCacheMissResume( () -> super.lookupRoute(exchange)) // 然后写到缓存 .andWriteWith( (key, signal) -> Mono.fromRunnable( () -> Optional.ofNullable(signal.get()) .ifPresent(value -> specialCache.put(key, value)) )); } return super.lookupRoute(exchange);} /** * 校验请求特征的方法,此处仅是举例 */ private boolean checkPath(String path) { return true;} /** * 生成cacheKey的方式,此处仅是举例 */ private String toKey(String specialPath) { return specialPath;}}
缓存管理配置(注意这里不宜采用 Redis 做缓存,因其性能会较低)
@Configuration@AutoConfigureBefore(CustomGatewayAutoConfiguration.class)public class CacheManagerConfiguration { @Bean @Primary public CacheManager defaultCacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); CaffeineSpec spec = CaffeineSpec .parse("initialCapacity=64,maximumSize=512,expireAfterWrite=300s"); cacheManager.setCacheNames(null); return cacheManager;} @Bean public CacheManager routeCacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); CaffeineSpec spec = CaffeineSpec .parse("initialCapacity=512,maximumSize=2048,expireAfterWrite=3000s"); cacheManager.setCacheNames(null); return cacheManager;}}
以上就简单实现了对 Gateway 的改造,结合业务场景进行具体的性能优化即可,优化后,在路由表较大时(大于 5000 条)能较为明显的提升网关路由性能。
至此修改完成,可以进行下一步测试验证。
测试结果
源码下载
测试记录
直连对照组
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 990.298 ± 219.989 ops/sMyBenchmark.testMethod avgt 20 0.002 ± 0.001 s/opMyBenchmark.testMethod sample 20205 0.002 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.001 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.011 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.017 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.017 s/opMyBenchmark.testMethod ss 20 0.002 ± 0.001 s/op
100 条路由(老版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 769.948 ± 112.572 ops/sMyBenchmark.testMethod avgt 20 0.003 ± 0.001 s/opMyBenchmark.testMethod sample 15364 0.003 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.008 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.015 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.015 s/opMyBenchmark.testMethod ss 20 0.003 ± 0.001 s/op
100 条路由(新版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 769.099 ± 110.400 ops/sMyBenchmark.testMethod avgt 20 0.003 ± 0.001 s/opMyBenchmark.testMethod sample 15541 0.003 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.008 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.012 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.012 s/opMyBenchmark.testMethod ss 20 0.003 ± 0.001 s/op
1K 条路由(老版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 759.265 ± 106.047 ops/sMyBenchmark.testMethod avgt 20 0.003 ± 0.001 s/opMyBenchmark.testMethod sample 15245 0.003 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.001 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.007 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.014 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.015 s/opMyBenchmark.testMethod ss 20 0.003 ± 0.001 s/op
1K 条路由(新版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 772.978 ± 102.976 ops/sMyBenchmark.testMethod avgt 20 0.003 ± 0.001 s/opMyBenchmark.testMethod sample 15101 0.003 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.007 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.016 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.016 s/opMyBenchmark.testMethod ss 20 0.003 ± 0.001 s/op
5K 条路由(老版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 232.624 ± 3.330 ops/sMyBenchmark.testMethod avgt 20 0.008 ± 0.001 s/opMyBenchmark.testMethod sample 4734 0.009 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.008 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.008 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.009 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.009 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.011 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.015 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.016 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.016 s/opMyBenchmark.testMethod ss 20 0.009 ± 0.001 s/op
5K 条路由(新版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 783.074 ± 112.114 ops/sMyBenchmark.testMethod avgt 20 0.003 ± 0.001 s/opMyBenchmark.testMethod sample 15318 0.003 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.001 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.007 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.017 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.017 s/opMyBenchmark.testMethod ss 20 0.003 ± 0.001 s/op
1W 条路由(老版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 122.122 ± 1.789 ops/sMyBenchmark.testMethod avgt 20 0.016 ± 0.001 s/opMyBenchmark.testMethod sample 2464 0.016 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.015 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.016 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.017 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.018 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.018 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.029 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.030 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.030 s/opMyBenchmark.testMethod ss 20 0.017 ± 0.001 s/op
1W 条路由(新版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 775.200 ± 121.410 ops/sMyBenchmark.testMethod avgt 20 0.003 ± 0.001 s/opMyBenchmark.testMethod sample 15261 0.003 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.001 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.003 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.007 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.014 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.014 s/opMyBenchmark.testMethod ss 20 0.003 ± 0.001 s/op
10W 条路由(老版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 12.765 ± 0.338 ops/sMyBenchmark.testMethod avgt 20 0.159 ± 0.006 s/opMyBenchmark.testMethod sample 260 0.153 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.147 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.152 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.157 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.159 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.163 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.167 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.167 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.167 s/opMyBenchmark.testMethod ss 20 0.155 ± 0.002 s/op
10W 条路由(新版本)
Benchmark Mode Cnt Score Error UnitsMyBenchmark.testMethod thrpt 20 774.979 ± 115.501 ops/sMyBenchmark.testMethod avgt 20 0.003 ± 0.001 s/opMyBenchmark.testMethod sample 15422 0.003 ± 0.001 s/opMyBenchmark.testMethod:testMethod·p0.00 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.50 sample 0.002 s/opMyBenchmark.testMethod:testMethod·p0.90 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.95 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.99 sample 0.004 s/opMyBenchmark.testMethod:testMethod·p0.999 sample 0.005 s/opMyBenchmark.testMethod:testMethod·p0.9999 sample 0.011 s/opMyBenchmark.testMethod:testMethod·p1.00 sample 0.012 s/opMyBenchmark.testMethod ss 20 0.003 ± 0.001 s/op
上一篇:
多线程四大经典案例