使用springcloud gateway搭建网关(分流,限流,熔断)

Spring Cloud Gateway

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

相关概念:

Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。

Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。

Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。

工作流程:

客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

Spring Cloud Gateway 的特征:

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0

动态路由

Predicates 和 Filters 作用于特定路由

集成 Hystrix 断路器

集成 Spring Cloud DiscoveryClient

易于编写的 Predicates 和 Filters

限流

路径重写

快速上手

引入spring-boot 2.1.1.RELEASE ,springcloud的版本为 Greenwich.M3

org.springframework.bootspring-boot-starter-parent2.1.1.RELEASE<!-- lookup parent from repository -->1.8Greenwich.M3org.springframework.cloudspring-cloud-dependencies${spring-cloud.version}pomimport

添加的依赖包如下

org.springframework.cloudspring-cloud-starter-gatewayorg.springframework.cloudspring-cloud-starter-netflix-hystrixorg.springframework.bootspring-boot-starter-webfluxorg.springframework.bootspring-boot-starter-data-redis-reactive

注意springcloud gateway使用的web框架为webflux,和springMVC不兼容。引入的限流组件是hystrix。redis底层不再使用jedis,而是lettuce。

路由断言

接下来就是配置了,可以使用java代码硬编码配置路由过滤器,也可以使用yml配置文件配置。下面我们首先介绍配置文件配置方式

application.yml

server.port:8082spring: 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;@ComponentpublicclassWrapperResponseFilterimplementsGlobalFilter,Ordered{@OverridepublicintgetOrder(){// -1 is response write filter, must be called before thatreturn-2; }@OverridepublicMono filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpResponse originalResponse = exchange.getResponse(); DataBufferFactory bufferFactory = originalResponse.bufferFactory(); ServerHttpResponseDecorator decoratedResponse =newServerHttpResponseDecorator(originalResponse) {@OverridepublicMono writeWith(Publisher body) {if(bodyinstanceofFlux) { Flux fluxBody = (Flux) body;returnsuper.writeWith(fluxBody.map(dataBuffer -> {// probably should reuse buffersbyte[] content =newbyte[dataBuffer.readableByteCount()]; dataBuffer.read(content);// 释放掉内存DataBufferUtils.release(dataBuffer); String rs =newString(content, Charset.forName("UTF-8")); Response response =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); })); }// if body is not a flux. never got there.returnsuper.writeWith(body); } };// replace response with decoratorreturnchain.filter(exchange.mutate().response(decoratedResponse).build()); }}

需要注意的是order需要小于-1,需要先于NettyWriteResponseFilter过滤器执行。

有了一个这样的过滤器,我们就可以统一返回报文格式了。

认证

以下提供一个简单的认证过滤器

package org.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;@ComponentpublicclassAuthFilterimplementsGlobalFilter{ @OverridepublicMonofilter(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)); }}

限流

springcloud gateway 为我们提供了限流过滤器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;/**

* User Request Rate Limiter filter. See https://stripe.com/blog/rate-limiters and

*/publicclassRateLimiterGatewayFilterFactoryextendsAbstractGatewayFilterFactory{publicstaticfinalString KEY_RESOLVER_KEY ="keyResolver";privatefinalRateLimiter defaultRateLimiter;privatefinalKeyResolver defaultKeyResolver;publicRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {super(Config.class);this.defaultRateLimiter = defaultRateLimiter;this.defaultKeyResolver = defaultKeyResolver; }publicKeyResolver getDefaultKeyResolver() {returndefaultKeyResolver; }publicRateLimiter getDefaultRateLimiter() {returndefaultRateLimiter; }@SuppressWarnings("unchecked")@OverridepublicGatewayFilter apply(Config config) { KeyResolver resolver = (config.keyResolver ==null) ? defaultKeyResolver : config.keyResolver; RateLimiter limiter = (config.rateLimiter ==null) ? defaultRateLimiter : config.rateLimiter;return(exchange, chain) -> { Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);returnresolver.resolve(exchange).flatMap(key ->//TODO:if key is empty?limiter.isAllowed(route.getId(), key).flatMap(response -> {for(Map.Entry header : response.getHeaders().entrySet()) { exchange.getResponse().getHeaders().add(header.getKey(), header.getValue()); }if(response.isAllowed()) {returnchain.filter(exchange); } ServerHttpResponse rs = exchange.getResponse(); Responsedata= new Response();data.setCode("101");data.setMessage("访问过快"); byte[] datas = JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8); DataBuffer buffer = rs.bufferFactory().wrap(datas); rs.setStatusCode(HttpStatus.UNAUTHORIZED); rs.getHeaders().add("Content-Type","application/json;charset=UTF-8");returnrs.writeWith(Mono.just(buffer)); })); }; }publicstaticclassConfig{privateKeyResolver keyResolver;privateRateLimiter rateLimiter;privateHttpStatus statusCode = HttpStatus.TOO_MANY_REQUESTS;publicKeyResolver getKeyResolver() {returnkeyResolver; }publicConfig setKeyResolver(KeyResolver keyResolver) {this.keyResolver = keyResolver;returnthis; }publicRateLimiter getRateLimiter() {returnrateLimiter; }publicConfig setRateLimiter(RateLimiter rateLimiter) {this.rateLimiter = rateLimiter;returnthis; }publicHttpStatus getStatusCode() {returnstatusCode; }publicConfig setStatusCode(HttpStatus statusCode) {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{publicstaticfinalString BEAN_NAME ="customKeyResolver";@OverridepublicMonoresolve(ServerWebExchange exchange){returnMono.just(getKey(exchange)); }/** *  *@paramexchange *@return*/privateStringgetKey(ServerWebExchange exchange){  LimitKey limitKey =newLimitKey();  limitKey.setApi(exchange.getRequest().getPath().toString()); limitKey.setBiz(exchange.getRequest().getQueryParams().getFirst("biz"));returnJSON.toJSONString(limitKey); }}

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

最后RedisRateLimiter我们也需要重写,因为不支持多级限流,原生的只会判断一个key。代码如下:

/**

* This uses a basic token bucket algorithm and relies on the fact that Redis scripts

* execute atomically. No other operations can run between fetching the count and

* writing the new count.

*/@OverridepublicMono isAllowed(StringrouteId,Stringid) {if(!this.initialized.get()) {thrownewIllegalStateException("RedisRateLimiter is not initialized"); } LimitConfig limitConfig = getLimitConfig(routeId);if(limitConfig ==null|| limitConfig.getTokenConfig().size()==0) {returnMono.just(newResponse(true,null)); } Map conf = limitConfig.getTokenConfig();  LimitKey limitKey = JSON.parseObject(id, LimitKey.class);//api限流Stringapi = limitKey.getApi(); Config apiConf = conf.get(api);//业务方限流Stringbiz = limitKey.getBiz(); Config bizConf = 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<>())); } } }/**

* 单级限流

* @param api

* @param routeId

* @param apiConf

* @return

*/privateMono isSingleAllow(Stringkey,StringrouteId, Config config) {// How many requests per second do you want a user to be allowed to do?intreplenishRate = config.getReplenishRate();// How much bursting do you want to allow?intburstCapacity = config.getBurstCapacity();try{ List keys = getKeys(routeId+"$"+key);// The arguments to the LUA script. time() returns unixtime in seconds.List scriptArgs = Arrays.asList(replenishRate +"", burstCapacity +"", Instant.now().getEpochSecond() +"","1");// allowed, tokens_left = redis.eval(SCRIPT, keys, args)Flux> flux =this.redisTemplate.execute(this.script, keys, scriptArgs);// .log("redisratelimiter", Level.FINER);returnflux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L,-1L))) .reduce(newArrayList(), (longs, l) -> { longs.addAll(l);returnlongs; }) .map(results -> {booleanallowed = results.get(0) ==1L; Long tokensLeft = results.get(1); Response response =newResponse(allowed, getHeaders(config, tokensLeft));if(log.isDebugEnabled()) {log.debug("response: "+ response); }returnresponse; }); }catch(Exception e) {/*

* We don't want a hard dependency on Redis to allow traffic. Make sure to set

* an alert so you know if this is happening too much. Stripe's observed

* failure rate is 0.01%.

*/log.error("Error determining if user allowed from redis", e); }returnMono.just(newResponse(true, getHeaders(config,-1L))); }privateLimitConfig getLimitConfig(StringrouteId) { Mapmap=newHashMap<>(); LimitConfig limitConfig =newLimitConfig(); limitConfig.setRouteId("rateLimit_route"); Map tokenMap =newHashMap<>(); Config apiConfig =newConfig(); apiConfig.setBurstCapacity(5); apiConfig.setReplenishRate(5);  Config bizConfig =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@Primarypublic CustomRedisRateLimiter customRedisRateLimiter(ReactiveRedisTemplate redisTemplate,@Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript> redisScript, Validator validator) {returnnewCustomRedisRateLimiter(redisTemplate, redisScript, validator); }  @BeanpublicRateLimiterGatewayFilterFactoryrateLimiterGatewayFilterFactory(CustomRedisRateLimiter customRedisRateLimiter, CustomKeyResolver customKeyResolver) {returnnewRateLimiterGatewayFilterFactory(customRedisRateLimiter, customKeyResolver); }

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

server.port:8082spring: 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:8082spring: 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;@RestControllerpublicclassFallbackController{@GetMapping("/fallback")publicResponse fallback() { Response response = new Response(); response.setCode("100"); response.setMessage("服务暂时不可用");returnresponse; }}

注意需要设置commandKey的超时时间。其他的hystrix配置请访问Hystrix wiki.

动态配置路由和过滤器

最后我们来看一下如何动态配置路由和过滤器。

定义路由实体

/**

* Gateway的路由定义模型

*/publicclass GatewayRouteDefinition {/**

* 路由的Id

*/privateStringid;/**

* 路由断言集合配置

*/privateList predicates =newArrayList<>();/**

* 路由过滤器集合配置

*/privateList filters =newArrayList<>();/**

* 路由规则转发的目标uri

*/privateStringuri;/**

* 路由执行的顺序

*/privateintorder=0;}

路由断言实体

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

/**

* 路由断言定义模型

*/publicclassGatewayPredicateDefinition{/**

* 断言对应的Name

*/privateStringname;/**

* 配置的断言规则

*/privateMap args = newLinkedHashMap<>();}

过滤器实体

/**

* 过滤器定义模型

*/publicclassGatewayFilterDefinition{/**

* Filter Name

*/privateStringname;/**

* 对应的路由规则

*/privateMap 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{@AutowiredprivateDynamicRouteServiceImpl dynamicRouteService;/** * 增加路由 *@paramgwdefinition *@return*/@PostMapping("/add")publicString add(@RequestBodyGatewayRouteDefinition gwdefinition) {try{ RouteDefinition definition = assembleRouteDefinition(gwdefinition);returnthis.dynamicRouteService.add(definition); }catch(Exception e) { e.printStackTrace(); }return"succss"; }@GetMapping("/delete/{id}")publicString delete(@PathVariableString id) {returnthis.dynamicRouteService.delete(id); }@PostMapping("/update")publicString update(@RequestBodyGatewayRouteDefinition gwdefinition) { RouteDefinition definition = assembleRouteDefinition(gwdefinition);returnthis.dynamicRouteService.update(definition); }privateRouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) { RouteDefinition definition = new RouteDefinition(); List pdList=new ArrayList<>(); definition.setId(gwdefinition.getId()); List gatewayPredicateDefinitionList=gwdefinition.getPredicates();for(GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) { PredicateDefinition predicate = new PredicateDefinition(); predicate.setArgs(gpDefinition.getArgs()); predicate.setName(gpDefinition.getName()); pdList.add(predicate); }  List gatewayFilterDefinitions = gwdefinition.getFilters(); List filterList = new ArrayList<>();if(!CollectionUtils.isEmpty(gatewayFilterDefinitions)) {for(GatewayFilterDefinition gatewayFilterDefinition : gatewayFilterDefinitions) { FilterDefinition filterDefinition = new FilterDefinition(); filterDefinition.setName(gatewayFilterDefinition.getName()); filterDefinition.setArgs(gatewayFilterDefinition.getArgs()); filterList.add(filterDefinition); } } definition.setPredicates(pdList); definition.setFilters(filterList); URI uri = 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;@ServicepublicclassDynamicRouteServiceImplimplementsApplicationEventPublisherAware{@AutowiredprivateRouteDefinitionWriter routeDefinitionWriter;privateApplicationEventPublisher publisher;/** * 增加路由 *@paramdefinition *@return*/publicString add(RouteDefinition definition) { routeDefinitionWriter.save(Mono.just(definition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));return"success"; }/** * 更新路由 *@paramdefinition *@return*/publicString update(RouteDefinition definition) {try{this.routeDefinitionWriter.delete(Mono.just(definition.getId())); }catch(Exception e) {return"update fail,not find route routeId: "+definition.getId(); }try{ routeDefinitionWriter.save(Mono.just(definition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));return"success"; }catch(Exception e) {return"update route fail"; } }/** * 删除路由 *@paramid *@return*/publicString delete(String id) {try{this.routeDefinitionWriter.delete(Mono.just(id));return"delete success"; }catch(Exception e) { e.printStackTrace();return"delete fail"; } }@Overridepublicvoid setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher = applicationEventPublisher; }}

上面 routeDefinitionWriter的实现默认是InMemoryRouteDefinitionRepository,将路由存在内存中,我们可以自己实现一个将路由存在redis中的repository。this.publisher.publishEvent(new RefreshRoutesEvent(this));则会将CachingRouteLocator中的路由缓存清空。以上只是springcloud gateway支持的一小部分功能。

如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 转载请标明出处:https://www.fangzhipeng.com本文出自方志朋的博客 在上一篇文章详细的介绍...
    方志朋阅读 2,588评论 0 14
  • 考虑一个问题,外部的应用如何来访问内部各种各样的微服务呢?在微服务架构中,后端服务往往不直接开放给调用端,而是通过...
    weisen阅读 2,093评论 1 9
  • 网关可以将服务和外网进行隔离起到一定的保护作用,同时服务间局域网通信更加便捷。而且在网关中可以做到限流,权限校验,...
    滔滔逐浪阅读 1,335评论 0 8
  • 早上7点钟出门,送儿子上学,然后去老公的单位把我的电动车跟他骑的自行车交换下,途中我会买好早点,到他们单位时间差不...
    Tina育儿阅读 142评论 0 3
  • 我是一个特别向往自由的人,希望可以去不同的地方,看不一样的风景,遇到不认识的人,尝试没有做过的东西。而滑雪正是我从...
    月半月半月月半阅读 176评论 0 0