微信客户端电脑版-灵图地图
![default gateway](/uploads/image/0875.jpg)
2023年4月3日发(作者:pokemonmemhack1 82)
SpringCloudgateway原理分析
SpringCloudgateway原理分析
前置说明
本⽂源码基于springcloudFinchley版本.以下分析仅代表个⼈的理解,如有错误,欢迎探讨.
使⽤说明
以spring⼀贯的作风,⽤户使⽤的模式都是约定俗成的,也就是引⼊spring-cloud-starter-gateway依赖,然后就可以愉快的⾃动配置了.但是这
⾥有⼀个要说的点,springcloudgateway是只能使⽤在webflux上⾯,也就是说如果引⼊的springmvc依赖,那么需要修改,不然只能使⽤
zuul了.
接下来就是根据⾃⼰的需求配置⼀些路由的策略或者条件
spring:
cloud:
gateway:
discovery:
locator:
enabled:false#表明gateway开启服务注册和发现的功能,并且springcloudgateway⾃动根据服务发现为每⼀个服务创建了⼀个router,这个router将
以服务名开头的请求路径转发到对应的服务。
lowerCaseServiceId:true#是将请求路径上的服务名配置为⼩写(因为服务注册的时候,向注册中⼼注册时将服务名转成⼤写的了),⽐如以/service-
hi/*的请求路径被路由转发到服务名为service-hi的服务上。
filters:
-StripPrefix=1
routes:
-id:client2
uri:lb://client2
predicates:
-Path=/client2/**
filters:
-StripPrefix=1
原理介绍
配置的加载
gateway的⾃动装配类主要在GatewayAutoConfiguration,其配置了GatewayProperties,FilteringWebHandler,RouteLocator,
RouteDefinitionLocator,以及过滤器⼯⼚和路由⼯⼚等bean的配置
然后,我们在配置⽂件中的路由配置的会被⾃动加载到GatewayProperties,然后在转换为RouteDefinition存储在GatewayProperties#routes中
下⾯的图是调试中⼀个RouteDefinition,其主要包含了predicate和filters
路由的获取
⾸先我们的⼊⼝在ctHandlerMapping#getHandler.
AbstractHandlerMapping这个类我想应该⼤家看着很熟,因为它很像springmvc⾥⾯的handlerMapping.我认为这是Spring团队想要webflux
和springmvc以相同的结构,这样⼤家可以没有太⼤的学习成本来上⼿.
这边接下来会⾛到redicateHandlerMapping#getHandlerInternal,这个类时gateway实现的
AbstractHandlerMapping来⽤于实现⽹关路由功能
@Override
protectedMono<?>getHandlerInternal(ServerWebExchangeexchange){
if(managmentPort!=null&&uest().getURI().getPort()==ue()){
();
}
ributes().put(GATEWAY_HANDLER_MAPPER_ATTR,getSimpleName());
returnlookupRoute(exchange)//超找所有的符合条件的路由
.flatMap((Function
ributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
ributes().put(GATEWAY_ROUTE_ATTR,r);
(webHandler);//返回要执⾏的过滤器执⾏器
}).switchIfEmpty(().then(nnable(()->{
ributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if(eEnabled()){
("NoRouteDefinitionfoundfor["+getExchangeDesc(exchange)+"]");
}
})));
}
protectedMono
ocator
.getRoutes()//从路由管理器中获取所有的路由
//转换和连接的操作符
.concatMap(route->Mono
.just(route)
.filterWhen(r->{//过滤符合条件的route
ributes().put(GATEWAY_PREDICATE_ROUTE_ATTR,());
dicate().apply(exchange);//判断路由是否符合条件
})
.doOnError(e->("Errorapplyingpredicateforroute:"+(),e))
.onErrorResume(e->())
)
.next()//返回第⼀个
.map(route->{
validateRoute(route,exchange);
returnroute;
});
}
上⾯看到,主要是通过tes的⽅法获取所有的路由,默认的话是通过RouteDefinitionRouteLocator,也就是通
过RouteDefinition的⽅式进⾏配置的路由.当然我们也可以通过⾃定义RouteLocator进⾏路由的提供,然后通过CompositeRouteLocator⽅式
容器会进⾏⾃⼰的组装.
下⾯看下efinitionRouteLocator#getRoutes
@Override
publicFlux
teDefinitions()//获取配置和持久化的路由
.map(this::convertToRoute)
//TODO:errorhandling
.map(route->{
if(gEnabled()){
("RouteDefinitionmatched:"+());
}
returnroute;
});
}
路由的话前⾯也提到了就是可以配置在配置⽂件中,也就是PropertiesRouteDefinitionLocator,当然spring也提供了其他的配置⽅式,⽐如
InMemoryRouteDefinitionRepository的⽅法,这个⽅式可以通过REST接⼝进⾏动态的添加了路由,也可以⾃定义持久化⽅式,进⾏数据库的存
储等.
publicclassInMemoryRouteDefinitionRepositoryimplementsRouteDefinitionRepository{
//可以看到只是存储在map中
privatefinalMap
@Override
publicMono
p(r->{
((),r);
();
});
}
@Override
publicMono
p(id->{
if(nsKey(id)){
(id);
();
}
(()->(newNotFoundException("RouteDefinitionnotfound:"+routeId)));
});
}
@Override
publicFlux
erable(());
}
}
路由对象的转换RouteDefinition->Route
privateRouteconvertToRoute(RouteDefinitionrouteDefinition){
AsyncPredicate
List
(routeDefinition)
.asyncPredicate(predicate)
.replaceFilters(gatewayFilters)
.build();
}
这⾥的话简单说⼀下,就是配置⽂件中的
predicates:
-Path=/client2/**这⾥的key=value,对应了⼀个RoutePredicateFactory
filters:
-StripPrefix=1这⾥的key=value,对应了⼀个GatewayFilterFactory
按照我的配置⽂件写的route包含的predicateFactory就是PathRoutePredicateFactory,filterFactory就是StripPrefixGatewayFilterFactory,
通过这两个⼯⼚会返回真正的predicate和filter.
流程的执⾏
上⾯的话已经讲到了请求进来根据URL来查找对应的handler,所以接下来就是判断路由以及执⾏过滤器的逻辑.
Predicate的执⾏逻辑前⾯有提到,就是redicateHandlerMapping#lookupRoute中的这⼀⾏
dicate().apply(exchange);//判断路由是否符合条件
这⾥可以看下我配置的predicate
@Override
publicPredicate
synchronized(tternParser){
chOptionalTrailingSeparator(hOptionalTrailingSeparator());
ttern=(n);
}
returnexchange->{
//解析路径
PathContainerpath=parsePath(uest().getURI().getPath());
//路径匹配
booleanmatch=s(path);
traceMatch("Pattern",ternString(),path,match);
if(match){
PathMatchInfouriTemplateVariables=ndExtract(path);
ributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE,uriTemplateVariables);
returntrue;
}else{
returnfalse;
}
};
}
接下来看下filter的执⾏,也就是webHandler的执⾏过程
ingWebHandler#handle
publicMono
Routeroute=uiredAttribute(GATEWAY_ROUTE_ATTR);
List
List
(gatewayFilters);//结合全局的过滤器
(combined);
if(gEnabled()){
("SortedgatewayFilterFactories:"+combined);
}
//构建过滤器链以及执⾏
returnnewDefaultGatewayFilterChain(combined).filter(exchange);
}
可以看下我这边配置ScriptFilter
@Override
publicGatewayFilterapply(Configconfig){
return(exchange,chain)->{
ServerHttpRequestrequest=uest();
addOriginalRequestUrl(exchange,());
Stringpath=().getRawPath();
StringnewPath="/"+(zeToStringArray(path,"/"))
.skip().collect(g("/"));//这⾥通过skip跳过指定的不需要的uri段
ServerHttpRequestnewRequest=()
.path(newPath)
.build();
ributes().put(GATEWAY_REQUEST_URL_ATTR,());
(().request(newRequest).build());
};
}
下⾯是进⾏负载均衡的过滤器LoadBalancerClientFilter
@Override
publicMono
URIurl=ribute(GATEWAY_REQUEST_URL_ATTR);
StringschemePrefix=ribute(GATEWAY_SCHEME_PREFIX_ATTR);
//这⾥就是我配置⽂件中uri:lb://client2
if(url==null||(!"lb".equals(eme())&&!"lb".equals(schemePrefix))){
(exchange);
}
//preservetheoriginalurl
addOriginalRequestUrl(exchange,url);
//根据负载均衡算法获取⼀个真正的远程服务器
finalServiceInstanceinstance=choose(exchange);
if(instance==null){
thrownewNotFoundException("Unabletofindinstancefor"+t());
}
URIuri=uest().getURI();
StringoverrideScheme=null;
if(schemePrefix!=null){
overrideScheme=eme();
}
//解析服务为具体的地址
URIrequestUrl=tructURI(newDelegatingServiceInstance(instance,overrideScheme),uri);
ributes().put(GATEWAY_REQUEST_URL_ATTR,requestUrl);
(exchange);
}
总结
主要的知识点
y的配置
2.路由的配置转换为routeDefinition
3.获取请求对应的路由规则,将RouteDefinition转换为Route
4.执⾏Predicate判断是否符合路由,以及执⾏相关的过滤(全局过滤器以及路由过滤器)
5.负载均衡过滤器负责将请求中的serviceId转换为具体的服务实例Ip
更多推荐
default gateway
发布评论