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 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

相关概念:

  • 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 的特征:

  • 基于 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

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.1.RELEASE</version><relativePath/> <!-- lookup parent from repository --></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>

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

路由断言

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

application.yml

server.port: 8082spring:application:name: gatewaycloud:gateway:routes:- id: path_routeuri: http://localhost:8000order: 0predicates:- 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

接下来我们来看一下设计一个网关应该需要的一些功能

修改接口返回报文

因为网关路由的接口返回报文格式各异,并且网关也有有一些限流、认证、熔断降级的返回报文,为了统一这些报文的返回格式,网关必须要对接口的返回报文进行修改,过滤器代码如下:

package org.gateway.filter.global;import java.nio.charset.Charset;import org.gateway.response.Response;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;import com.alibaba.fastjson.JSON;import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;@Component
public class WrapperResponseFilter implements GlobalFilter, Ordered {@Overridepublic int getOrder() {// -1 is response write filter, must be called before thatreturn -2;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpResponse originalResponse = exchange.getResponse();DataBufferFactory bufferFactory = originalResponse.bufferFactory();ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {if (body instanceof Flux) {Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;return super.writeWith(fluxBody.map(dataBuffer -> {// probably should reuse buffersbyte[] content = new byte[dataBuffer.readableByteCount()];dataBuffer.read(content);// 释放掉内存
                        DataBufferUtils.release(dataBuffer);String rs = new String(content, Charset.forName("UTF-8"));Response response = new Response();response.setCode("1");response.setMessage("请求成功");response.setData(rs);byte[] newRs = JSON.toJSONString(response).getBytes(Charset.forName("UTF-8"));originalResponse.getHeaders().setContentLength(newRs.length);//如果不重新设置长度则收不到消息。return bufferFactory.wrap(newRs);}));}// if body is not a flux. never got there.return super.writeWith(body);}};// replace response with decoratorreturn chain.filter(exchange.mutate().response(decoratedResponse).build());}
}

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

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

认证

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

package org.gateway.filter.global;import java.nio.charset.StandardCharsets;import org.gateway.response.Response;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;import com.alibaba.fastjson.JSON;import reactor.core.publisher.Mono;@Component
public class AuthFilter implements GlobalFilter{@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String token = exchange.getRequest().getHeaders().getFirst("token");if ("token".equals(token)) {return chain.filter(exchange);}ServerHttpResponse response = exchange.getResponse();Response data = new Response();data.setCode("401");data.setMessage("非法请求");byte[] datas = JSON.toJSONString(data).getBytes(StandardCharsets.UTF_8);DataBuffer buffer = response.bufferFactory().wrap(datas);response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");return response.writeWith(Mono.just(buffer));}
}

限流

springcloud gateway 为我们提供了限流过滤器RequestRateLimiterGatewayFilterFactory,和限流的实现类RedisRateLimiter使用令牌桶限流。但是官方的不一定满足我们的需求,所以我们重新写一个过滤器(基本和官方一致),只是将官方的返回报文改了。

package org.gateway.limiter;import java.nio.charset.StandardCharsets;
import java.util.Map;import org.gateway.response.Response;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;import com.alibaba.fastjson.JSON;import reactor.core.publisher.Mono;/*** User Request Rate Limiter filter. See https://stripe.com/blog/rate-limiters and*/
public class RateLimiterGatewayFilterFactory extends AbstractGatewayFilterFactory<RateLimiterGatewayFilterFactory.Config> {public static final String KEY_RESOLVER_KEY = "keyResolver";private final RateLimiter defaultRateLimiter;private final KeyResolver defaultKeyResolver;public RateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter,KeyResolver defaultKeyResolver) {super(Config.class);this.defaultRateLimiter = defaultRateLimiter;this.defaultKeyResolver = defaultKeyResolver;}public KeyResolver getDefaultKeyResolver() {return defaultKeyResolver;}public RateLimiter getDefaultRateLimiter() {return defaultRateLimiter;}@SuppressWarnings("unchecked")@Overridepublic GatewayFilter apply(Config config) {KeyResolver resolver = (config.keyResolver == null) ? defaultKeyResolver : config.keyResolver;RateLimiter<Object> limiter = (config.rateLimiter == null) ? defaultRateLimiter : config.rateLimiter;return (exchange, chain) -> {Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);return resolver.resolve(exchange).flatMap(key ->// TODO: if key is empty?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()) {return chain.filter(exchange);}ServerHttpResponse rs = exchange.getResponse();Response data = 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");return rs.writeWith(Mono.just(buffer));}));};}public static class Config {private KeyResolver keyResolver;private RateLimiter rateLimiter;private HttpStatus statusCode = HttpStatus.TOO_MANY_REQUESTS;public KeyResolver getKeyResolver() {return keyResolver;}public Config setKeyResolver(KeyResolver keyResolver) {this.keyResolver = keyResolver;return this;}public RateLimiter getRateLimiter() {return rateLimiter;}public Config setRateLimiter(RateLimiter rateLimiter) {this.rateLimiter = rateLimiter;return this;}public HttpStatus getStatusCode() {return statusCode;}public Config setStatusCode(HttpStatus statusCode) {this.statusCode = statusCode;return this;}}}

然后限流必须要有一个key,根据什么来进行限流,ip,接口,或者用户来进行限流,所以我们自定义一个KeyResolver

package org.gateway.limiter;import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.web.server.ServerWebExchange;import com.alibaba.fastjson.JSON;import reactor.core.publisher.Mono;public class CustomKeyResolver implements KeyResolver {public static final String BEAN_NAME = "customKeyResolver";@Overridepublic Mono<String> resolve(ServerWebExchange exchange) {return Mono.just(getKey(exchange));}/*** * @param exchange* @return*/private String getKey(ServerWebExchange exchange) {LimitKey limitKey = new LimitKey();limitKey.setApi(exchange.getRequest().getPath().toString());limitKey.setBiz(exchange.getRequest().getQueryParams().getFirst("biz"));return JSON.toJSONString(limitKey);}
}

最后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.*/@Overridepublic Mono<Response> isAllowed(String routeId, String id) {if (!this.initialized.get()) {throw new IllegalStateException("RedisRateLimiter is not initialized");}LimitConfig limitConfig = getLimitConfig(routeId);if (limitConfig == null || limitConfig.getTokenConfig().size()==0) {return Mono.just(new Response(true,null));}Map<String, Config> conf = limitConfig.getTokenConfig();LimitKey limitKey = JSON.parseObject(id, LimitKey.class);//api限流String api = limitKey.getApi();Config apiConf = conf.get(api);//业务方限流String biz = limitKey.getBiz();Config bizConf = conf.get(biz);if (apiConf!=null) {return isSingleAllow(api,routeId,apiConf).flatMap(res -> {if (res.isAllowed()) {if(bizConf!=null) {return isSingleAllow(biz, routeId, bizConf);}else {return Mono.just(new Response(true,new HashMap<>()));}}else {return Mono.just(res);}} );}else {if (bizConf!=null) {return isSingleAllow(biz, routeId, bizConf);}else {return Mono.just(new Response(true,new HashMap<>()));}}}/*** 单级限流* @param api* @param routeId* @param apiConf* @return */private Mono<Response> isSingleAllow(String key, String routeId, Config config) {// How many requests per second do you want a user to be allowed to do?int replenishRate = config.getReplenishRate();// How much bursting do you want to allow?int burstCapacity = config.getBurstCapacity();try {List<String> keys = getKeys(routeId+"$"+key);// The arguments to the LUA script. time() returns unixtime in seconds.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);return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))).reduce(new ArrayList<Long>(), (longs, l) -> {longs.addAll(l);return longs;}) .map(results -> {boolean allowed = results.get(0) == 1L;Long tokensLeft = results.get(1);Response response = new Response(allowed, getHeaders(config, tokensLeft));if (log.isDebugEnabled()) {log.debug("response: " + response);}return response;});}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);}return Mono.just(new Response(true, getHeaders(config, -1L)));}private LimitConfig getLimitConfig(String routeId) {Map<String, LimitConfig> map = new HashMap<>();LimitConfig limitConfig = new LimitConfig();limitConfig.setRouteId("rateLimit_route");Map<String, Config> tokenMap = new HashMap<>();Config apiConfig = new Config();apiConfig.setBurstCapacity(5);apiConfig.setReplenishRate(5);Config bizConfig = new Config();bizConfig.setBurstCapacity(1);bizConfig.setReplenishRate(1);tokenMap.put("/hello/rateLimit", apiConfig);tokenMap.put("jieyin", bizConfig);limitConfig.setTokenConfig(tokenMap);map.put("rateLimit_route", limitConfig);return limitConfig;}

如上的代码是写死的,但是我们可以根据我们的业务需求设计一个自定义key,自定义令牌桶容量和速率的限流规则。

bean配置和yml配置如下

    @Bean@Primarypublic CustomRedisRateLimiter customRedisRateLimiter(ReactiveRedisTemplate<String, String> redisTemplate,@Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript<List<Long>> redisScript,Validator validator) {return new CustomRedisRateLimiter(redisTemplate, redisScript, validator);}@Beanpublic RateLimiterGatewayFilterFactory rateLimiterGatewayFilterFactory(CustomRedisRateLimiter customRedisRateLimiter, CustomKeyResolver customKeyResolver) {return new RateLimiterGatewayFilterFactory(customRedisRateLimiter, customKeyResolver);}

server.port: 8082spring:application:name: gatewayredis:host: localhostport: 6379password: 123456cloud:gateway:routes:- id: rateLimit_routeuri: http://localhost:8000order: 0predicates:- Path=/foo/**filters:- StripPrefix=1- name: RateLimiter

熔断

当下游接口负载很大,或者接口不通等其他原因导致超时,如果接口不熔断的话将会影响到下游接口得不到喘息,网关也会因为超时连接一直挂起,很可能因为一个子系统的问题导致整个系统的雪崩。所以我们的网关需要设计熔断,当因为熔断器打开时,网关将返回一个降级的应答。

熔断配置如下:

server.port: 8082spring:application:name: gatewayredis:host: localhostport: 6379password: 123456cloud:gateway:routes:- id: rateLimit_routeuri: http://localhost:8000order: 0predicates:- Path=/foo/**filters:- StripPrefix=1- name: RateLimiter- name: Hystrixargs:name: fallbackcmdfallbackUri: forward:/fallback

hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000

package org.gateway.controller;import org.gateway.response.Response;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class FallbackController {@GetMapping("/fallback")public Response fallback() {Response response = new Response();response.setCode("100");response.setMessage("服务暂时不可用");return response;}
}

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

动态配置路由和过滤器

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

定义路由实体

/*** Gateway的路由定义模型*/
public class GatewayRouteDefinition {/*** 路由的Id*/private String id;/*** 路由断言集合配置*/private List<GatewayPredicateDefinition> predicates = new ArrayList<>();/*** 路由过滤器集合配置*/private List<GatewayFilterDefinition> filters = new ArrayList<>();/*** 路由规则转发的目标uri*/private String uri;/*** 路由执行的顺序*/private int order = 0;
}

路由断言实体

/*** 路由断言定义模型*/
public class GatewayPredicateDefinition {/*** 断言对应的Name*/private String name;/*** 配置的断言规则*/private Map<String, String> args = new LinkedHashMap<>();}

过滤器实体

/*** 过滤器定义模型*/
public class GatewayFilterDefinition {/*** Filter Name*/private String name;/*** 对应的路由规则*/private Map<String, String> args = new LinkedHashMap<>();
}

路由增删改controller

package org.gateway.controller;import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.gateway.model.GatewayFilterDefinition;
import org.gateway.model.GatewayPredicateDefinition;
import org.gateway.model.GatewayRouteDefinition;
import org.gateway.route.DynamicRouteServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriComponentsBuilder;@RestController
@RequestMapping("/route")
public class RouteController {@Autowiredprivate DynamicRouteServiceImpl dynamicRouteService;/*** 增加路由* @param gwdefinition* @return*/@PostMapping("/add")public String add(@RequestBody GatewayRouteDefinition gwdefinition) {try {RouteDefinition definition = assembleRouteDefinition(gwdefinition);return this.dynamicRouteService.add(definition);} catch (Exception e) {e.printStackTrace();}return "succss";}@GetMapping("/delete/{id}")public String delete(@PathVariable String id) {return this.dynamicRouteService.delete(id);}@PostMapping("/update")public String update(@RequestBody GatewayRouteDefinition gwdefinition) {RouteDefinition definition = assembleRouteDefinition(gwdefinition);return this.dynamicRouteService.update(definition);}private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) {RouteDefinition definition = new RouteDefinition();List<PredicateDefinition> pdList=new ArrayList<>();definition.setId(gwdefinition.getId());List<GatewayPredicateDefinition> gatewayPredicateDefinitionList=gwdefinition.getPredicates();for (GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) {PredicateDefinition predicate = new PredicateDefinition();predicate.setArgs(gpDefinition.getArgs());predicate.setName(gpDefinition.getName());pdList.add(predicate);}List<GatewayFilterDefinition> gatewayFilterDefinitions = gwdefinition.getFilters();List<FilterDefinition> 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);return definition;}
}

动态路由service

package org.gateway.route;import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;import org.gateway.model.GatewayPredicateDefinition;
import org.gateway.model.GatewayRouteDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;import com.alibaba.fastjson.JSON;import reactor.core.publisher.Mono;@Service
public class DynamicRouteServiceImpl implements ApplicationEventPublisherAware {@Autowiredprivate RouteDefinitionWriter routeDefinitionWriter;private ApplicationEventPublisher publisher;/*** 增加路由* @param definition* @return*/public String add(RouteDefinition definition) {routeDefinitionWriter.save(Mono.just(definition)).subscribe();this.publisher.publishEvent(new RefreshRoutesEvent(this));return "success";}/*** 更新路由* @param definition* @return*/public String 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";}}/*** 删除路由* @param id* @return*/public String delete(String id) {try {this.routeDefinitionWriter.delete(Mono.just(id));return "delete success";} catch (Exception e) {e.printStackTrace();return "delete fail";}}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.publisher = applicationEventPublisher;}
}

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

虽然springcloud gateway 才发布不久,相关的文档还不是很完善,代码中充满了TODO的地方,react代码友好性低。但是由于它的高性能而且是spring自己的框架,未来取代zuul不是没有可能。

转载于:https://www.cnblogs.com/qianwei/p/10127700.html

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

  1. SpringCloud Gateway 服务网关,限流

    SpringCloud Gateway 提供了基于Redis 和lua脚本实现的令牌桶算法进行限流,即 RequestRateLimiterGatewayFilterFactory类,通过设置过滤器实 ...

  2. 秒杀项目之网关服务限流熔断降级分布式事务

    目录 一.网关服务限流熔断降级 二.Seata--分布式事务 2.1 分布式事务基础 2.1.1 事务 2.1.2 本地事务 2.1.3 分布式事务 2.1.4 分布式事务场景 2.2 分布式事务解决 ...

  3. SpringCloud Gateway 通过redis实现限流【SpringCloud系列8】

    SpringCloud 大型系列课程正在制作中,欢迎大家关注与提意见. 程序员每天的CV 与 板砖,也要知其所以然,本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCl ...

  4. SpringCloud Gateway 通过redis实现限流

    前言 在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击. 常见的限流方式,比如Hystrix适用线程池隔离,超过线程池的负载 ...

  5. 深度好文 — 微服务和API网关限流熔断实现关键逻辑思路

    来源:https://www.toutiao.com/i6853970319745483275/?group_id=6853970319745483275 今天准备谈下微服务架构和API网关中的限流熔 ...

  6. Nacos,Sentine限流熔断,Gateway网关

    文章目录 建立项目 注册中心简介 背景分析 Nacos概述 构建Nacos服务 准备工作 If use MySQL as datasource: Count of DB: Connect URL of ...

  7. SpringCloud Alibaba微服务实战(五) - Sentinel实现限流熔断

    什么是Sentinel? 请查看文章:SpringCloud Alibaba微服务实战(一) - 基础环境搭建 构建服务消费者cloud-sentinel进行服务调用 服务创建请查看文章:Spring ...

  8. Spring Cloud Gateway 原生的接口限流该怎么玩

    为什么80%的码农都做不了架构师?>>>    关于pig: 基于Spring Cloud.oAuth2.0开发基于Vue前后分离的开发平台,支持账号.短信.SSO等多种登录,提供配 ...

  9. Spring Cloud Gateway 原生支持接口限流该怎么玩

    关于pig: 基于Spring Cloud.oAuth2.0开发基于Vue前后分离的开发平台,支持账号.短信.SSO等多种登录,提供配套视频开发教程. 关于 Spring Cloud Gateway ...

最新文章

  1. 各种小的 dp (精)
  2. 基于python的一个运维自动化的项目(进度更新)【已开源】
  3. 大脑进化追不上社会文化:化石和脱氧核糖核酸证明人类大脑进化比社会慢
  4. [原创]Paros工具培训介绍
  5. _extend用法总结
  6. 【2021年】通过vue-cli创建electron项目
  7. iOS 真机调试包集合 包含 iOS 12.4 不断更新
  8. css3图标悬停导航菜单
  9. Linux常用命令——chattr、lsattr
  10. Jakarta Struts应用的七个经验
  11. 写一个在一个字符串(n)中寻找一个子串(m)第一个位置的函数。
  12. 牛客编程巅峰赛S2第7场 - 钻石王者
  13. 采用文件白名单方式构建主机安全环境
  14. xshell网站打不开
  15. 时间序列分析ARMA模型-学习笔记
  16. 2022年各国程序员薪资排名!
  17. PostGIS几何图形操作
  18. Skype for Business Web 应用
  19. php env 函数不存在,PHP编程:探究Laravel使用env函数读取环境变量为null的问题
  20. 软件构造期末复习知识点整理

热门文章

  1. Android 4.4 Settings 应用初步分析
  2. Android Broadcast Security
  3. arcgis 字段计算器 条件赋值_【教程】ArcGIS入门教程(11)——多条件购房分析...
  4. python3urllib中的quote与encode的区别_Python的urllib.quote()和urllib.unquote()的等效Javascript函数...
  5. JZOJ 5402. 【NOIP2017提高A组模拟10.8】God Knows
  6. 自媒体敏感词大全_2020年自媒体软件大全,这里真的有大宝贝……
  7. iTunes只能装C盘吗_C盘清理简单汇总
  8. Qt 视图框架示例 Colliding Mice 的翻译
  9. python string模块安装_python String模块-阿里云开发者社区
  10. [转载]出了国才明白的10件事~(MITBBS ZT)