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: 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

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

修改接口返回报文

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

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 {
@Override
public int getOrder() {
// -1 is response write filter, must be called before that
return -2;
} @Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public 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 buffers
byte[] 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 decorator
return 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{
@Override
public 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")
@Override
public 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"; @Override
public 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.
*/
@Override
public 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
@Primary
public CustomRedisRateLimiter customRedisRateLimiter(ReactiveRedisTemplate<String, String> redisTemplate,
@Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript<List<Long>> redisScript,
Validator validator) {
return new CustomRedisRateLimiter(redisTemplate, redisScript, validator);
} @Bean
public RateLimiterGatewayFilterFactory rateLimiterGatewayFilterFactory(CustomRedisRateLimiter customRedisRateLimiter, CustomKeyResolver customKeyResolver) {
return new RateLimiterGatewayFilterFactory(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

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 { @Autowired
private 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 { @Autowired
private 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";
} } @Override
public 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不是没有可能。

使用springcloud gateway搭建网关(分流,限流,熔断)的更多相关文章

  1. .Net微服务实践(四)[网关]:Ocelot限流熔断、缓存以及负载均衡

    目录 限流 熔断 缓存 Header转化 HTTP方法转换 负载均衡 注入/重写中间件 后台管理 最后 在上篇.Net微服务实践(三)[网关]:Ocelot配置路由和请求聚合中我们介绍了Ocelot的 ...

  2. Spring Cloud Gateway 结合配置中心限流

    前言 上篇文章我讲过复杂的限流场景可以通过扩展RedisRateLimiter来实现自己的限流策略. 假设你领导给你安排了一个任务,具体需求如下: 针对具体的接口做限流 不同接口限流的力度可以不同 可 ...

  3. Spring Cloud Gateway 扩展支持动态限流

    之前分享过 一篇 <Spring Cloud Gateway 原生的接口限流该怎么玩>, 核心是依赖Spring Cloud Gateway 默认提供的限流过滤器来实现 原生Request ...

  4. .net core使用ocelot---第四篇 限流熔断

    简介 .net core使用ocelot---第一篇 简单使用 .net core使用ocelot---第二篇 身份验证 .net core使用ocelot---第三篇 日志记录 前几篇文章我们陆续介 ...

  5. springBoot整合Sentinel实现降级限流熔断

    由于hystrix的停止更新,以及阿里Sentinel在历年双十一的贡献.项目中使用了Sentinel,今天我们来讲讲Sentinel的入门教程,本文使用1.6.3版本进行讲解 本文通过Sentine ...

  6. Hystrix介绍以及服务的降级限流熔断

    (dubbo熔断,Hystrix问的少) 无论是缓存层还是存储层都会有出错的概率,可以将它们视同为资源.作为并发量较大的系统,假如有一个资源不可用,可能会造成线程全部 hang (挂起)在这个资源上, ...

  7. DBPack 限流熔断功能发布说明

    上周我们发布了 v0.4.0 版本,增加了限流熔断功能,现对这两个功能做如下说明. 限流 DBPack 限流熔断功能通过 filter 实现.要设置限流规则,首先要定义 RateLimitFilter ...

  8. Spring Cloud alibaba网关 sentinel zuul 四 限流熔断

    spring cloud alibaba 集成了 他内部开源的 Sentinel 熔断限流框架 Sentinel 介绍 官方网址 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentine ...

  9. 深入学习spring cloud gateway 限流熔断

    前言 Spring Cloud Gateway 目前,Spring Cloud Gateway是仅次于Spring Cloud Netflix的第二个最受欢迎的Spring Cloud项目(就GitH ...

随机推荐

  1. Docker容器的自动化监控实现

    本文由  网易云 发布. 近年来容器技术不断成熟并得到应用.Docker作为容器技术的一个代表,目前也在快速发展中,基于 Docker的各种应用也正在普及,与此同时 Docker对传统的运维体系也带来 ...

  2. 近期js

    1 var value1 = 0, value2 = 0, value3 = 0; for ( var i = 1; i <= 3; i++) { var i2 = i; (function() ...

  3. JDBC连接数据库7个步骤

    JDBC连接数据库    •创建一个以JDBC连接数据库的程序,包含7个步骤:    1.JDBC所需的四个参数(user,password,url,driverClass) (1)user用户名 ( ...

  4. js获取时间的函数集

    var mydate = new Date(); mydate.getYear(); //获取当前年份(2位) mydate.getFullYear(); //获取完整的年份(4位,1970-???? ...

  5. Java虚拟机执行引擎

    执行引擎 关于执行引擎相关的部分, 在之前的博文里 Java内存区域中已经有所提及. 回顾一下: 也只有几个概念, JVM方法调用和执行的基础数据结构是 栈帧, 是内存区域中 虚拟机栈中的栈元素, 每 ...

  6. POJ 1131

    #include <iostream> #include <string> using namespace std; ]; int main() { //freopen(&qu ...

  7. rabbitmq使用日记

    一.安装 添加安装源 #echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list ...

  8. rabbitmq系列一 之简单队列

    1. rabbitmq简介 rabbitmq是一个消息代理,或者讲是一个消息中间件.主要是用来接收和转发信息的,它是对消息不做任何处理的.MQ是消费-生产者模型的一个典型的代表,一端往消息队列中不断写 ...

  9. Python生成gexf文件并导入gephi做网络图分析

    Gephi是一款优秀的复杂网络分析软件,支持导入多种格式的文件.gexf格式是Gephi 推荐的格式,基于 XML.本文是一个用python写的简单Demo,示例如何生成一个典型的gexf格式文件.代 ...

  10. 剑指offer四十六之孩子们的游戏(圆圈中最后剩下的数,约瑟夫环问题)

    一.题目 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此.HF作为牛客的资深元老,自然也准备了一些小游戏.其中,有个游戏是这样的:首先,让小朋友们围成一个大圈.然后,他随机指 ...