使用springcloud gateway搭建网关(分流,限流,熔断)
本文内容纲要:
-SpringCloudGateway
-快速上手
-路由断言
-修改接口返回报文
-认证
-限流
-熔断
-动态配置路由和过滤器
SpringCloudGateway
SpringCloudGateway是SpringCloud的一个全新项目,该项目是基于Spring5.0,SpringBoot2.0和ProjectReactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。
SpringCloudGateway作为SpringCloud生态系统中的网关,目标是替代NetflixZuul,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
相关概念:
- Route(路由):这是网关的基本构建块。它由一个ID,一个目标URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
- Predicate(断言):这是一个Java8的Predicate。输入类型是一个ServerWebExchange。我们可以使用它来匹配来自HTTP请求的任何内容,例如headers或参数。
- Filter(过滤器):这是
org.springframework.cloud.gateway.filter.GatewayFilter
的实例,我们可以使用它修改请求和响应。
工作流程:
客户端向SpringCloudGateway发出请求。如果GatewayHandlerMapping中找到与请求相匹配的路由,将其发送到GatewayWebHandler。Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
SpringCloudGateway的特征:
- 基于SpringFramework5,ProjectReactor和SpringBoot2.0
- 动态路由
- Predicates和Filters作用于特定路由
- 集成Hystrix断路器
- 集成SpringCloudDiscoveryClient
- 易于编写的Predicates和Filters
- 限流
- 路径重写
快速上手
引入spring-boot2.1.1.RELEASE,springcloud的版本为Greenwich.M3
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/><!--lookupparentfromrepository-->
</parent>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.M3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
添加的依赖包如下
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
注意springcloudgateway使用的web框架为webflux,和springMVC不兼容。引入的限流组件是hystrix。redis底层不再使用jedis,而是lettuce。
路由断言
接下来就是配置了,可以使用java代码硬编码配置路由过滤器,也可以使用yml配置文件配置。下面我们首先介绍配置文件配置方式
application.yml
server.port:8082
spring:
application:
name:gateway
cloud:
gateway:
routes:
-id:path_route
uri:http://localhost:8000
order:0
predicates:
-Path=/foo/**
filters:
-StripPrefix=1
上面给出了一个根据请求路径来匹配目标uri的例子,如果请求的路径为/foo/bar,则目标uri为http://localhost:8000/bar。如果上面例子中没有加一个StripPrefix=1过滤器,则目标uri为http://localhost:8000/foo/bar,StripPrefix过滤器是去掉一个路径。
其他的路由断言和过滤器使用方法请查看官网
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RC2/single/spring-cloud-gateway.html#gateway-how-it-works
接下来我们来看一下设计一个网关应该需要的一些功能
修改接口返回报文
因为网关路由的接口返回报文格式各异,并且网关也有有一些限流、认证、熔断降级的返回报文,为了统一这些报文的返回格式,网关必须要对接口的返回报文进行修改,过滤器代码如下:
packageorg.gateway.filter.global;
importjava.nio.charset.Charset;
importorg.gateway.response.Response;
importorg.reactivestreams.Publisher;
importorg.springframework.cloud.gateway.filter.GatewayFilterChain;
importorg.springframework.cloud.gateway.filter.GlobalFilter;
importorg.springframework.core.Ordered;
importorg.springframework.core.io.buffer.DataBuffer;
importorg.springframework.core.io.buffer.DataBufferFactory;
importorg.springframework.core.io.buffer.DataBufferUtils;
importorg.springframework.http.server.reactive.ServerHttpResponse;
importorg.springframework.http.server.reactive.ServerHttpResponseDecorator;
importorg.springframework.stereotype.Component;
importorg.springframework.web.server.ServerWebExchange;
importcom.alibaba.fastjson.JSON;
importreactor.core.publisher.Flux;
importreactor.core.publisher.Mono;
@Component
publicclassWrapperResponseFilterimplementsGlobalFilter,Ordered{
@Override
publicintgetOrder(){
//-1isresponsewritefilter,mustbecalledbeforethat
return-2;
}
@Override
publicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){
ServerHttpResponseoriginalResponse=exchange.getResponse();
DataBufferFactorybufferFactory=originalResponse.bufferFactory();
ServerHttpResponseDecoratordecoratedResponse=newServerHttpResponseDecorator(originalResponse){
@Override
publicMono<Void>writeWith(Publisher<?extendsDataBuffer>body){
if(bodyinstanceofFlux){
Flux<?extendsDataBuffer>fluxBody=(Flux<?extendsDataBuffer>)body;
returnsuper.writeWith(fluxBody.map(dataBuffer->{
//probablyshouldreusebuffers
byte[]content=newbyte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
//释放掉内存
DataBufferUtils.release(dataBuffer);
Stringrs=newString(content,Charset.forName("UTF-8"));
Responseresponse=newResponse();
response.setCode("1");
response.setMessage("请求成功");
response.setData(rs);
byte[]newRs=JSON.toJSONString(response).getBytes(Charset.forName("UTF-8"));
originalResponse.getHeaders().setContentLength(newRs.length);//如果不重新设置长度则收不到消息。
returnbufferFactory.wrap(newRs);
}));
}
//ifbodyisnotaflux.nevergotthere.
returnsuper.writeWith(body);
}
};
//replaceresponsewithdecorator
returnchain.filter(exchange.mutate().response(decoratedResponse).build());
}
}
需要注意的是order需要小于-1,需要先于NettyWriteResponseFilter过滤器执行。
有了一个这样的过滤器,我们就可以统一返回报文格式了。
认证
以下提供一个简单的认证过滤器
packageorg.gateway.filter.global;
importjava.nio.charset.StandardCharsets;
importorg.gateway.response.Response;
importorg.springframework.cloud.gateway.filter.GatewayFilterChain;
importorg.springframework.cloud.gateway.filter.GlobalFilter;
importorg.springframework.core.io.buffer.DataBuffer;
importorg.springframework.http.HttpStatus;
importorg.springframework.http.server.reactive.ServerHttpResponse;
importorg.springframework.stereotype.Component;
importorg.springframework.web.server.ServerWebExchange;
importcom.alibaba.fastjson.JSON;
importreactor.core.publisher.Mono;
@Component
publicclassAuthFilterimplementsGlobalFilter{
@Override
publicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){
Stringtoken=exchange.getRequest().getHeaders().getFirst("token");
if("token".equals(token)){
returnchain.filter(exchange);
}
ServerHttpResponseresponse=exchange.getResponse();
Responsedata=newResponse();
data.setCode("401");
data.setMessage("非法请求");
byte[]datas=JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8);
DataBufferbuffer=response.bufferFactory().wrap(datas);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
returnresponse.writeWith(Mono.just(buffer));
}
}
限流
springcloudgateway为我们提供了限流过滤器RequestRateLimiterGatewayFilterFactory,和限流的实现类RedisRateLimiter使用令牌桶限流。但是官方的不一定满足我们的需求,所以我们重新写一个过滤器(基本和官方一致),只是将官方的返回报文改了。
packageorg.gateway.limiter;
importjava.nio.charset.StandardCharsets;
importjava.util.Map;
importorg.gateway.response.Response;
importorg.springframework.cloud.gateway.filter.GatewayFilter;
importorg.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
importorg.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
importorg.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
importorg.springframework.cloud.gateway.route.Route;
importorg.springframework.cloud.gateway.support.ServerWebExchangeUtils;
importorg.springframework.core.io.buffer.DataBuffer;
importorg.springframework.http.HttpStatus;
importorg.springframework.http.server.reactive.ServerHttpResponse;
importcom.alibaba.fastjson.JSON;
importreactor.core.publisher.Mono;
/**
*UserRequestRateLimiterfilter.Seehttps://stripe.com/blog/rate-limitersand
*/
publicclassRateLimiterGatewayFilterFactoryextendsAbstractGatewayFilterFactory<RateLimiterGatewayFilterFactory.Config>{
publicstaticfinalStringKEY_RESOLVER_KEY="keyResolver";
privatefinalRateLimiterdefaultRateLimiter;
privatefinalKeyResolverdefaultKeyResolver;
publicRateLimiterGatewayFilterFactory(RateLimiterdefaultRateLimiter,
KeyResolverdefaultKeyResolver){
super(Config.class);
this.defaultRateLimiter=defaultRateLimiter;
this.defaultKeyResolver=defaultKeyResolver;
}
publicKeyResolvergetDefaultKeyResolver(){
returndefaultKeyResolver;
}
publicRateLimitergetDefaultRateLimiter(){
returndefaultRateLimiter;
}
@SuppressWarnings("unchecked")
@Override
publicGatewayFilterapply(Configconfig){
KeyResolverresolver=(config.keyResolver==null)?defaultKeyResolver:config.keyResolver;
RateLimiter<Object>limiter=(config.rateLimiter==null)?defaultRateLimiter:config.rateLimiter;
return(exchange,chain)->{
Routeroute=exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
returnresolver.resolve(exchange).flatMap(key->
//TODO:ifkeyisempty?
limiter.isAllowed(route.getId(),key).flatMap(response->{
for(Map.Entry<String,String>header:response.getHeaders().entrySet()){
exchange.getResponse().getHeaders().add(header.getKey(),header.getValue());
}
if(response.isAllowed()){
returnchain.filter(exchange);
}
ServerHttpResponsers=exchange.getResponse();
Responsedata=newResponse();
data.setCode("101");
data.setMessage("访问过快");
byte[]datas=JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8);
DataBufferbuffer=rs.bufferFactory().wrap(datas);
rs.setStatusCode(HttpStatus.UNAUTHORIZED);
rs.getHeaders().add("Content-Type","application/json;charset=UTF-8");
returnrs.writeWith(Mono.just(buffer));
}));
};
}
publicstaticclassConfig{
privateKeyResolverkeyResolver;
privateRateLimiterrateLimiter;
privateHttpStatusstatusCode=HttpStatus.TOO_MANY_REQUESTS;
publicKeyResolvergetKeyResolver(){
returnkeyResolver;
}
publicConfigsetKeyResolver(KeyResolverkeyResolver){
this.keyResolver=keyResolver;
returnthis;
}
publicRateLimitergetRateLimiter(){
returnrateLimiter;
}
publicConfigsetRateLimiter(RateLimiterrateLimiter){
this.rateLimiter=rateLimiter;
returnthis;
}
publicHttpStatusgetStatusCode(){
returnstatusCode;
}
publicConfigsetStatusCode(HttpStatusstatusCode){
this.statusCode=statusCode;
returnthis;
}
}
}
然后限流必须要有一个key,根据什么来进行限流,ip,接口,或者用户来进行限流,所以我们自定义一个KeyResolver
packageorg.gateway.limiter;
importorg.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
importorg.springframework.web.server.ServerWebExchange;
importcom.alibaba.fastjson.JSON;
importreactor.core.publisher.Mono;
publicclassCustomKeyResolverimplementsKeyResolver{
publicstaticfinalStringBEAN_NAME="customKeyResolver";
@Override
publicMono<String>resolve(ServerWebExchangeexchange){
returnMono.just(getKey(exchange));
}
/**
*
*@paramexchange
*@return
*/
privateStringgetKey(ServerWebExchangeexchange){
LimitKeylimitKey=newLimitKey();
limitKey.setApi(exchange.getRequest().getPath().toString());
limitKey.setBiz(exchange.getRequest().getQueryParams().getFirst("biz"));
returnJSON.toJSONString(limitKey);
}
}
最后RedisRateLimiter我们也需要重写,因为不支持多级限流,原生的只会判断一个key。代码如下:
/**
*ThisusesabasictokenbucketalgorithmandreliesonthefactthatRedisscripts
*executeatomically.Nootheroperationscanrunbetweenfetchingthecountand
*writingthenewcount.
*/
@Override
publicMono<Response>isAllowed(StringrouteId,Stringid){
if(!this.initialized.get()){
thrownewIllegalStateException("RedisRateLimiterisnotinitialized");
}
LimitConfiglimitConfig=getLimitConfig(routeId);
if(limitConfig==null||limitConfig.getTokenConfig().size()==0){
returnMono.just(newResponse(true,null));
}
Map<String,Config>conf=limitConfig.getTokenConfig();
LimitKeylimitKey=JSON.parseObject(id,LimitKey.class);
//api限流
Stringapi=limitKey.getApi();
ConfigapiConf=conf.get(api);
//业务方限流
Stringbiz=limitKey.getBiz();
ConfigbizConf=conf.get(biz);
if(apiConf!=null){
returnisSingleAllow(api,routeId,apiConf).flatMap(res->{
if(res.isAllowed()){
if(bizConf!=null){
returnisSingleAllow(biz,routeId,bizConf);
}else{
returnMono.just(newResponse(true,newHashMap<>()));
}
}else{
returnMono.just(res);
}
});
}else{
if(bizConf!=null){
returnisSingleAllow(biz,routeId,bizConf);
}else{
returnMono.just(newResponse(true,newHashMap<>()));
}
}
}
/**
*单级限流
*@paramapi
*@paramrouteId
*@paramapiConf
*@return
*/
privateMono<Response>isSingleAllow(Stringkey,StringrouteId,Configconfig){
//Howmanyrequestsperseconddoyouwantausertobeallowedtodo?
intreplenishRate=config.getReplenishRate();
//Howmuchburstingdoyouwanttoallow?
intburstCapacity=config.getBurstCapacity();
try{
List<String>keys=getKeys(routeId+"$"+key);
//TheargumentstotheLUAscript.time()returnsunixtimeinseconds.
List<String>scriptArgs=Arrays.asList(replenishRate+"",burstCapacity+"",
Instant.now().getEpochSecond()+"","1");
//allowed,tokens_left=redis.eval(SCRIPT,keys,args)
Flux<List<Long>>flux=this.redisTemplate.execute(this.script,keys,scriptArgs);
//.log("redisratelimiter",Level.FINER);
returnflux.onErrorResume(throwable->Flux.just(Arrays.asList(1L,-1L)))
.reduce(newArrayList<Long>(),(longs,l)->{
longs.addAll(l);
returnlongs;
}).map(results->{
booleanallowed=results.get(0)==1L;
LongtokensLeft=results.get(1);
Responseresponse=newResponse(allowed,getHeaders(config,tokensLeft));
if(log.isDebugEnabled()){
log.debug("response:"+response);
}
returnresponse;
});
}
catch(Exceptione){
/*
*Wedon'twantaharddependencyonRedistoallowtraffic.Makesuretoset
*analertsoyouknowifthisishappeningtoomuch.Stripe'sobserved
*failurerateis0.01%.
*/
log.error("Errordeterminingifuserallowedfromredis",e);
}
returnMono.just(newResponse(true,getHeaders(config,-1L)));
}
privateLimitConfiggetLimitConfig(StringrouteId){
Map<String,LimitConfig>map=newHashMap<>();
LimitConfiglimitConfig=newLimitConfig();
limitConfig.setRouteId("rateLimit_route");
Map<String,Config>tokenMap=newHashMap<>();
ConfigapiConfig=newConfig();
apiConfig.setBurstCapacity(5);
apiConfig.setReplenishRate(5);
ConfigbizConfig=newConfig();
bizConfig.setBurstCapacity(1);
bizConfig.setReplenishRate(1);
tokenMap.put("/hello/rateLimit",apiConfig);
tokenMap.put("jieyin",bizConfig);
limitConfig.setTokenConfig(tokenMap);
map.put("rateLimit_route",limitConfig);
returnlimitConfig;
}
如上的代码是写死的,但是我们可以根据我们的业务需求设计一个自定义key,自定义令牌桶容量和速率的限流规则。
bean配置和yml配置如下
@Bean
@Primary
publicCustomRedisRateLimitercustomRedisRateLimiter(ReactiveRedisTemplate<String,String>redisTemplate,
@Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME)RedisScript<List<Long>>redisScript,
Validatorvalidator){
returnnewCustomRedisRateLimiter(redisTemplate,redisScript,validator);
}
@Bean
publicRateLimiterGatewayFilterFactoryrateLimiterGatewayFilterFactory(CustomRedisRateLimitercustomRedisRateLimiter,CustomKeyResolvercustomKeyResolver){
returnnewRateLimiterGatewayFilterFactory(customRedisRateLimiter,customKeyResolver);
}
server.port:8082
spring:
application:
name:gateway
redis:
host:localhost
port:6379
password:123456
cloud:
gateway:
routes:
-id:rateLimit_route
uri:http://localhost:8000
order:0
predicates:
-Path=/foo/**
filters:
-StripPrefix=1
-name:RateLimiter
熔断
当下游接口负载很大,或者接口不通等其他原因导致超时,如果接口不熔断的话将会影响到下游接口得不到喘息,网关也会因为超时连接一直挂起,很可能因为一个子系统的问题导致整个系统的雪崩。所以我们的网关需要设计熔断,当因为熔断器打开时,网关将返回一个降级的应答。
熔断配置如下:
server.port:8082
spring:
application:
name:gateway
redis:
host:localhost
port:6379
password:123456
cloud:
gateway:
routes:
-id:rateLimit_route
uri:http://localhost:8000
order:0
predicates:
-Path=/foo/**
filters:
-StripPrefix=1
-name:RateLimiter
-name:Hystrix
args:
name:fallbackcmd
fallbackUri:forward:/fallback
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds:5000
packageorg.gateway.controller;
importorg.gateway.response.Response;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RestController;
@RestController
publicclassFallbackController{
@GetMapping("/fallback")
publicResponsefallback(){
Responseresponse=newResponse();
response.setCode("100");
response.setMessage("服务暂时不可用");
returnresponse;
}
}
注意需要设置commandKey的超时时间。其他的hystrix配置请访问Hystrixwiki.
动态配置路由和过滤器
最后我们来看一下如何动态配置路由和过滤器。
定义路由实体
/**
*Gateway的路由定义模型
*/
publicclassGatewayRouteDefinition{
/**
*路由的Id
*/
privateStringid;
/**
*路由断言集合配置
*/
privateList<GatewayPredicateDefinition>predicates=newArrayList<>();
/**
*路由过滤器集合配置
*/
privateList<GatewayFilterDefinition>filters=newArrayList<>();
/**
*路由规则转发的目标uri
*/
privateStringuri;
/**
*路由执行的顺序
*/
privateintorder=0;
}
路由断言实体
/**
*路由断言定义模型
*/
publicclassGatewayPredicateDefinition{
/**
*断言对应的Name
*/
privateStringname;
/**
*配置的断言规则
*/
privateMap<String,String>args=newLinkedHashMap<>();
}
过滤器实体
/**
*过滤器定义模型
*/
publicclassGatewayFilterDefinition{
/**
*FilterName
*/
privateStringname;
/**
*对应的路由规则
*/
privateMap<String,String>args=newLinkedHashMap<>();
}
路由增删改controller
packageorg.gateway.controller;
importjava.net.URI;
importjava.util.ArrayList;
importjava.util.Arrays;
importjava.util.HashMap;
importjava.util.List;
importjava.util.Map;
importorg.gateway.model.GatewayFilterDefinition;
importorg.gateway.model.GatewayPredicateDefinition;
importorg.gateway.model.GatewayRouteDefinition;
importorg.gateway.route.DynamicRouteServiceImpl;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.cloud.gateway.filter.FilterDefinition;
importorg.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
importorg.springframework.cloud.gateway.route.RouteDefinition;
importorg.springframework.util.CollectionUtils;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.PathVariable;
importorg.springframework.web.bind.annotation.PostMapping;
importorg.springframework.web.bind.annotation.RequestBody;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
importorg.springframework.web.util.UriComponentsBuilder;
@RestController
@RequestMapping("/route")
publicclassRouteController{
@Autowired
privateDynamicRouteServiceImpldynamicRouteService;
/**
*增加路由
*@paramgwdefinition
*@return
*/
@PostMapping("/add")
publicStringadd(@RequestBodyGatewayRouteDefinitiongwdefinition){
try{
RouteDefinitiondefinition=assembleRouteDefinition(gwdefinition);
returnthis.dynamicRouteService.add(definition);
}catch(Exceptione){
e.printStackTrace();
}
return"succss";
}
@GetMapping("/delete/{id}")
publicStringdelete(@PathVariableStringid){
returnthis.dynamicRouteService.delete(id);
}
@PostMapping("/update")
publicStringupdate(@RequestBodyGatewayRouteDefinitiongwdefinition){
RouteDefinitiondefinition=assembleRouteDefinition(gwdefinition);
returnthis.dynamicRouteService.update(definition);
}
privateRouteDefinitionassembleRouteDefinition(GatewayRouteDefinitiongwdefinition){
RouteDefinitiondefinition=newRouteDefinition();
List<PredicateDefinition>pdList=newArrayList<>();
definition.setId(gwdefinition.getId());
List<GatewayPredicateDefinition>gatewayPredicateDefinitionList=gwdefinition.getPredicates();
for(GatewayPredicateDefinitiongpDefinition:gatewayPredicateDefinitionList){
PredicateDefinitionpredicate=newPredicateDefinition();
predicate.setArgs(gpDefinition.getArgs());
predicate.setName(gpDefinition.getName());
pdList.add(predicate);
}
List<GatewayFilterDefinition>gatewayFilterDefinitions=gwdefinition.getFilters();
List<FilterDefinition>filterList=newArrayList<>();
if(!CollectionUtils.isEmpty(gatewayFilterDefinitions)){
for(GatewayFilterDefinitiongatewayFilterDefinition:gatewayFilterDefinitions){
FilterDefinitionfilterDefinition=newFilterDefinition();
filterDefinition.setName(gatewayFilterDefinition.getName());
filterDefinition.setArgs(gatewayFilterDefinition.getArgs());
filterList.add(filterDefinition);
}
}
definition.setPredicates(pdList);
definition.setFilters(filterList);
URIuri=UriComponentsBuilder.fromHttpUrl(gwdefinition.getUri()).build().toUri();
definition.setUri(uri);
returndefinition;
}
}
动态路由service
packageorg.gateway.route;
importjava.net.URI;
importjava.util.Arrays;
importjava.util.HashMap;
importjava.util.Map;
importorg.gateway.model.GatewayPredicateDefinition;
importorg.gateway.model.GatewayRouteDefinition;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.cloud.gateway.event.RefreshRoutesEvent;
importorg.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
importorg.springframework.cloud.gateway.route.RouteDefinition;
importorg.springframework.cloud.gateway.route.RouteDefinitionWriter;
importorg.springframework.context.ApplicationEventPublisher;
importorg.springframework.context.ApplicationEventPublisherAware;
importorg.springframework.stereotype.Service;
importorg.springframework.web.util.UriComponentsBuilder;
importcom.alibaba.fastjson.JSON;
importreactor.core.publisher.Mono;
@Service
publicclassDynamicRouteServiceImplimplementsApplicationEventPublisherAware{
@Autowired
privateRouteDefinitionWriterrouteDefinitionWriter;
privateApplicationEventPublisherpublisher;
/**
*增加路由
*@paramdefinition
*@return
*/
publicStringadd(RouteDefinitiondefinition){
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(newRefreshRoutesEvent(this));
return"success";
}
/**
*更新路由
*@paramdefinition
*@return
*/
publicStringupdate(RouteDefinitiondefinition){
try{
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
}catch(Exceptione){
return"updatefail,notfindrouterouteId:"+definition.getId();
}
try{
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(newRefreshRoutesEvent(this));
return"success";
}catch(Exceptione){
return"updateroutefail";
}
}
/**
*删除路由
*@paramid
*@return
*/
publicStringdelete(Stringid){
try{
this.routeDefinitionWriter.delete(Mono.just(id));
return"deletesuccess";
}catch(Exceptione){
e.printStackTrace();
return"deletefail";
}
}
@Override
publicvoidsetApplicationEventPublisher(ApplicationEventPublisherapplicationEventPublisher){
this.publisher=applicationEventPublisher;
}
}
上面routeDefinitionWriter的实现默认是InMemoryRouteDefinitionRepository,将路由存在内存中,我们可以自己实现一个将路由存在redis中的repository。
this.publisher.publishEvent(newRefreshRoutesEvent(this));则会将CachingRouteLocator中的路由缓存清空。
以上只是springcloudgateway支持的一小部分功能。
虽然springcloudgateway才发布不久,相关的文档还不是很完善,代码中充满了TODO的地方,react代码友好性低。但是由于它的高性能而且是spring自己的框架,未来取代zuul不是没有可能。
本文内容总结:SpringCloudGateway,快速上手,路由断言,修改接口返回报文,认证,限流,熔断,动态配置路由和过滤器,
原文链接:https://www.cnblogs.com/qianwei/p/10127700.html