使用springcloud gateway搭建网关(分流,限流,熔断)
Spring Cloud Gateway
Spring Cloud Gateway 是 Spring Cloud 的一個(gè)全新項(xiàng)目,該項(xiàng)目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術(shù)開(kāi)發(fā)的網(wǎng)關(guān),它旨在為微服務(wù)架構(gòu)提供一種簡(jiǎn)單有效的統(tǒng)一的 API 路由管理方式。
Spring Cloud Gateway 作為 Spring Cloud 生態(tài)系統(tǒng)中的網(wǎng)關(guān),目標(biāo)是替代 Netflix Zuul,其不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全,監(jiān)控/指標(biāo),和限流。
相關(guān)概念:
- Route(路由):這是網(wǎng)關(guān)的基本構(gòu)建塊。它由一個(gè) ID,一個(gè)目標(biāo) URI,一組斷言和一組過(guò)濾器定義。如果斷言為真,則路由匹配。
- Predicate(斷言):這是一個(gè) Java 8 的 Predicate。輸入類(lèi)型是一個(gè) ServerWebExchange。我們可以使用它來(lái)匹配來(lái)自 HTTP 請(qǐng)求的任何內(nèi)容,例如 headers 或參數(shù)。
- Filter(過(guò)濾器):這是org.springframework.cloud.gateway.filter.GatewayFilter的實(shí)例,我們可以使用它修改請(qǐng)求和響應(yīng)。
工作流程:
客戶(hù)端向 Spring Cloud Gateway 發(fā)出請(qǐng)求。如果 Gateway Handler Mapping 中找到與請(qǐng)求相匹配的路由,將其發(fā)送到 Gateway Web Handler。Handler 再通過(guò)指定的過(guò)濾器鏈來(lái)將請(qǐng)求發(fā)送到我們實(shí)際的服務(wù)執(zhí)行業(yè)務(wù)邏輯,然后返回。 過(guò)濾器之間用虛線分開(kāi)是因?yàn)檫^(guò)濾器可能會(huì)在發(fā)送代理請(qǐng)求之前(“pre”)或之后(“post”)執(zhí)行業(yè)務(wù)邏輯。
Spring Cloud Gateway 的特征:
- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
- 動(dòng)態(tài)路由
- Predicates 和 Filters 作用于特定路由
- 集成 Hystrix 斷路器
- 集成 Spring Cloud DiscoveryClient
- 易于編寫(xiě)的 Predicates 和 Filters
- 限流
- 路徑重寫(xiě)
?快速上手
引入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>?添加的依賴(lài)包如下
<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。
路由斷言
?接下來(lái)就是配置了,可以使用java代碼硬編碼配置路由過(guò)濾器,也可以使用yml配置文件配置。下面我們首先介紹配置文件配置方式
application.yml
server.port: 8082spring:application:name: gatewaycloud:gateway:routes:- id: path_routeuri: http://localhost:8000order: 0predicates:- Path=/foo/**filters:- StripPrefix=1?上面給出了一個(gè)根據(jù)請(qǐng)求路徑來(lái)匹配目標(biāo)uri的例子,如果請(qǐng)求的路徑為/foo/bar,則目標(biāo)uri為?http://localhost:8000/bar。如果上面例子中沒(méi)有加一個(gè)StripPrefix=1過(guò)濾器,則目標(biāo)uri 為http://localhost:8000/foo/bar,StripPrefix過(guò)濾器是去掉一個(gè)路徑。
?其他的路由斷言和過(guò)濾器使用方法請(qǐng)查看官網(wǎng)
?https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RC2/single/spring-cloud-gateway.html#gateway-how-it-works
?接下來(lái)我們來(lái)看一下設(shè)計(jì)一個(gè)網(wǎng)關(guān)應(yīng)該需要的一些功能
修改接口返回報(bào)文
因?yàn)榫W(wǎng)關(guān)路由的接口返回報(bào)文格式各異,并且網(wǎng)關(guān)也有有一些限流、認(rèn)證、熔斷降級(jí)的返回報(bào)文,為了統(tǒng)一這些報(bào)文的返回格式,網(wǎng)關(guān)必須要對(duì)接口的返回報(bào)文進(jìn)行修改,過(guò)濾器代碼如下:
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);// 釋放掉內(nèi)存 DataBufferUtils.release(dataBuffer);String rs = new String(content, Charset.forName("UTF-8"));Response response = new Response();response.setCode("1");response.setMessage("請(qǐng)求成功");response.setData(rs);byte[] newRs = JSON.toJSONString(response).getBytes(Charset.forName("UTF-8"));originalResponse.getHeaders().setContentLength(newRs.length);//如果不重新設(shè)置長(zhǎng)度則收不到消息。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過(guò)濾器執(zhí)行。
有了一個(gè)這樣的過(guò)濾器,我們就可以統(tǒng)一返回報(bào)文格式了。
認(rèn)證
以下提供一個(gè)簡(jiǎn)單的認(rèn)證過(guò)濾器
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("非法請(qǐng)求");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 為我們提供了限流過(guò)濾器RequestRateLimiterGatewayFilterFactory,和限流的實(shí)現(xiàn)類(lèi)RedisRateLimiter使用令牌桶限流。但是官方的不一定滿(mǎn)足我們的需求,所以我們重新寫(xiě)一個(gè)過(guò)濾器(基本和官方一致),只是將官方的返回報(bào)文改了。
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("訪問(wèn)過(guò)快");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;}}}?然后限流必須要有一個(gè)key,根據(jù)什么來(lái)進(jìn)行限流,ip,接口,或者用戶(hù)來(lái)進(jìn)行限流,所以我們自定義一個(gè)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我們也需要重寫(xiě),因?yàn)椴恢С侄嗉?jí)限流,原生的只會(huì)判斷一個(gè)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);//業(yè)務(wù)方限流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<>()));}}}/*** 單級(jí)限流* @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;}?如上的代碼是寫(xiě)死的,但是我們可以根據(jù)我們的業(yè)務(wù)需求設(shè)計(jì)一個(gè)自定義key,自定義令牌桶容量和速率的限流規(guī)則。
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熔斷
? ? ? 當(dāng)下游接口負(fù)載很大,或者接口不通等其他原因?qū)е鲁瑫r(shí),如果接口不熔斷的話(huà)將會(huì)影響到下游接口得不到喘息,網(wǎng)關(guān)也會(huì)因?yàn)槌瑫r(shí)連接一直掛起,很可能因?yàn)橐粋€(gè)子系統(tǒng)的問(wèn)題導(dǎo)致整個(gè)系統(tǒng)的雪崩。所以我們的網(wǎng)關(guān)需要設(shè)計(jì)熔斷,當(dāng)因?yàn)槿蹟嗥鞔蜷_(kāi)時(shí),網(wǎng)關(guān)將返回一個(gè)降級(jí)的應(yīng)答。
熔斷配置如下:
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("服務(wù)暫時(shí)不可用");return response;} }?
注意需要設(shè)置commandKey的超時(shí)時(shí)間。其他的hystrix配置請(qǐng)?jiān)L問(wèn)Hystrix wiki.
動(dòng)態(tài)配置路由和過(guò)濾器
最后我們來(lái)看一下如何動(dòng)態(tài)配置路由和過(guò)濾器。
定義路由實(shí)體
/*** Gateway的路由定義模型*/ public class GatewayRouteDefinition {/*** 路由的Id*/private String id;/*** 路由斷言集合配置*/private List<GatewayPredicateDefinition> predicates = new ArrayList<>();/*** 路由過(guò)濾器集合配置*/private List<GatewayFilterDefinition> filters = new ArrayList<>();/*** 路由規(guī)則轉(zhuǎn)發(fā)的目標(biāo)uri*/private String uri;/*** 路由執(zhí)行的順序*/private int order = 0; }?
路由斷言實(shí)體
/*** 路由斷言定義模型*/ public class GatewayPredicateDefinition {/*** 斷言對(duì)應(yīng)的Name*/private String name;/*** 配置的斷言規(guī)則*/private Map<String, String> args = new LinkedHashMap<>();}
?
過(guò)濾器實(shí)體
/*** 過(guò)濾器定義模型*/ public class GatewayFilterDefinition {/*** Filter Name*/private String name;/*** 對(duì)應(yīng)的路由規(guī)則*/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;} }?動(dòng)態(tài)路由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的實(shí)現(xiàn)默認(rèn)是InMemoryRouteDefinitionRepository,將路由存在內(nèi)存中,我們可以自己實(shí)現(xiàn)一個(gè)將路由存在redis中的repository。 this.publisher.publishEvent(new RefreshRoutesEvent(this));則會(huì)將CachingRouteLocator中的路由緩存清空。以上只是springcloud gateway支持的一小部分功能。
雖然springcloud gateway 才發(fā)布不久,相關(guān)的文檔還不是很完善,代碼中充滿(mǎn)了TODO的地方,react代碼友好性低。但是由于它的高性能而且是spring自己的框架,未來(lái)取代zuul不是沒(méi)有可能。
轉(zhuǎn)載于:https://www.cnblogs.com/qianwei/p/10127700.html
總結(jié)
以上是生活随笔為你收集整理的使用springcloud gateway搭建网关(分流,限流,熔断)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 打包java程序生成exe
- 下一篇: gentoo rt-thread sco