微信客户端电脑版-灵图地图

default gateway
2023年4月3日发(作者:pokemonmemhack1 82)

SpringCloudgateway原理分析

SpringCloudgateway原理分析

前置说明

本⽂源码基于springcloudFinchley版本.以下分析仅代表个⼈的理解,如有错误,欢迎探讨.

使⽤说明

以spring⼀贯的作风,⽤户使⽤的模式都是约定俗成的,也就是引⼊spring-cloud-starter-gateway依赖,然后就可以愉快的⾃动配置了.但是这

⾥有⼀个要说的点,springcloudgateway是只能使⽤在webflux上⾯,也就是说如果引⼊的springmvc依赖,那么需要修改,不然只能使⽤

zuul了.

spring-cloud-starter-gateway

接下来就是根据⾃⼰的需求配置⼀些路由的策略或者条件

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>)r->{

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)+"]");

}

})));

}

protectedMonolookupRoute(ServerWebExchangeexchange){

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

publicFluxgetRoutes(){

teDefinitions()//获取配置和持久化的路由

.map(this::convertToRoute)

//TODO:errorhandling

.map(route->{

if(gEnabled()){

("RouteDefinitionmatched:"+());

}

returnroute;

});

}

路由的话前⾯也提到了就是可以配置在配置⽂件中,也就是PropertiesRouteDefinitionLocator,当然spring也提供了其他的配置⽅式,⽐如

InMemoryRouteDefinitionRepository的⽅法,这个⽅式可以通过REST接⼝进⾏动态的添加了路由,也可以⾃定义持久化⽅式,进⾏数据库的存

储等.

publicclassInMemoryRouteDefinitionRepositoryimplementsRouteDefinitionRepository{

//可以看到只是存储在map中

privatefinalMaproutes=synchronizedMap(newLinkedHashMap());

@Override

publicMonosave(Monoroute){

p(r->{

((),r);

();

});

}

@Override

publicMonodelete(MonorouteId){

p(id->{

if(nsKey(id)){

(id);

();

}

(()->(newNotFoundException("RouteDefinitionnotfound:"+routeId)));

});

}

@Override

publicFluxgetRouteDefinitions(){

erable(());

}

}

路由对象的转换RouteDefinition->Route

privateRouteconvertToRoute(RouteDefinitionrouteDefinition){

AsyncPredicatepredicate=combinePredicates(routeDefinition);//获取路由判断器

ListgatewayFilters=getFilters(routeDefinition);//获取该路由要的过滤器

(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

publicPredicateapply(Configconfig){

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

publicMonohandle(ServerWebExchangeexchange){

Routeroute=uiredAttribute(GATEWAY_ROUTE_ATTR);

ListgatewayFilters=ters();//获取该路由的过滤器

Listcombined=newArrayList<>(Filters);

(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

publicMonofilter(ServerWebExchangeexchange,GatewayFilterChainchain){

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