使用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的實例,我們可以使用它修改請求和響應。

工作流程:

使用springcloud gateway搭建網關(分流,限流,熔斷)

客戶端向 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

 <parent>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-parent/<artifactid>
<version>2.1.1.RELEASE/<version>
<relativepath>
/<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());
}
}
/<void>/<void>

需要注意的是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));
}
}
/<void>

限流

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> 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;
}
}
}
/<string>/<object>/<ratelimitergatewayfilterfactory.config>

然後限流必須要有一個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);
}
}
/<string>

如果想學習Java工程化、高性能及分佈式、深入淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群裡有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給大家。

最後RedisRateLimiter我們也需要重寫,因為不支持多級限流,原生的只會判斷一個key。代碼如下:

 /**
* This uses a basic token bucket algorithm and relies on the fact that Redis/> * 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> 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/> List<string>/> Instant.now().getEpochSecond() + "", "1");
// allowed, tokens_left = redis.eval(SCRIPT, keys, args)
Flux<list>> flux = this.redisTemplate.execute(this.script, keys,/> // .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> map = new HashMap<>();
LimitConfig limitConfig = new LimitConfig();
limitConfig.setRouteId("rateLimit_route");
Map<string> 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;
}
/<string>/<string>/<long>/<list>/<string>/<string>/<response>/<string>/<response>

如上的代碼是寫死的,但是我們可以根據我們的業務需求設計一個自定義key,自定義令牌桶容量和速率的限流規則。

bean配置和yml配置如下

 @Bean
@Primary
public CustomRedisRateLimiter customRedisRateLimiter(ReactiveRedisTemplate<string> redisTemplate,
@Qualifier(RedisRateLimiter.REDIS_SCRIPT_NAME) RedisScript<list>> redisScript,
Validator validator) {
return new CustomRedisRateLimiter(redisTemplate, redisScript, validator);
}

@Bean
public RateLimiterGatewayFilterFactory rateLimiterGatewayFilterFactory(CustomRedisRateLimiter customRedisRateLimiter, CustomKeyResolver customKeyResolver) {
return new RateLimiterGatewayFilterFactory(customRedisRateLimiter, customKeyResolver);
}
/<list>/<string>

如果想學習Java工程化、高性能及分佈式、深入淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群裡有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給大家。

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;
}
/<gatewayfilterdefinition>/<gatewaypredicatedefinition>

路由斷言實體

如果想學習Java工程化、高性能及分佈式、深入淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群裡有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給大家。

/**
* 路由斷言定義模型
*/
public class GatewayPredicateDefinition {
/**
* 斷言對應的Name
*/
private String name;
/**
* 配置的斷言規則
*/
private Map<string> args = new LinkedHashMap<>();
}
/<string>

過濾器實體

/**
* 過濾器定義模型
*/
public class GatewayFilterDefinition {
/**
* Filter Name
*/
private String name;
/**
* 對應的路由規則
*/
private Map<string> args = new LinkedHashMap<>();
}
/<string>

路由增刪改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;
}
}
/<filterdefinition>/<gatewayfilterdefinition>/<gatewaypredicatedefinition>/<predicatedefinition>

動態路由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支持的一小部分功能。

如果想學習Java工程化、高性能及分佈式、深入淺出。微服務、Spring,MyBatis,Netty源碼分析的朋友可以加我的Java高級交流:854630135,群裡有阿里大牛直播講解技術,以及Java大型互聯網技術的視頻免費分享給大家。


分享到:


相關文章: