一、概述
spring-cloud-gateway是一个库,可以在spring webflux软件开发定制定制之上建立一个网关,软件开发定制定制它的目的是提供一个简单,软件开发定制定制有效的方式去路由到APIS并且提供:安全、软件开发定制定制监控和弹性。gateway构建于spring boot2.x,spring webFlux,and Project Reactor,所以,软件开发定制定制许多同步库如spring Data和spring security不适用于gateway项目中。因为gateway软件开发定制定制是异步非阻塞的。
gateway要求spring boot和spring webFlux提供Netty运行环境。软件开发定制定制它不能工作在传统的servlet软件开发定制定制容器或打成一个War包。
二、特点
1、软件开发定制定制能够对任意的请求属性软件开发定制定制进行路由匹配
2、能对路由进行断言和过滤
3、集成了熔断器
4、集成了spring-cloud-discoveryclient
5、很容易的写断言和过滤
6、限流
7、路径重写
三、三大组件
1、Route。是构建的基本模块,他是ID,目标URL,一系列的断言和过滤器组成,如果断言为true,则匹配该路由
2、Rredicate。参考的是Java8的Predicate,开发人员可以匹配HTTP请求中的所有内容,如果请求与断言相匹配则进行路由
3、Filter。指的是spring框架中GatewayFileter的实例,使用过滤器,可以在请求被路由前由前或者之后对请求进行修改。
四、gateway的工作流程
客户端将请求发给spring cloud gateway,如果gateway handle mapping 确定这个请求和一个路由匹配,它将此请求发给 gateway web handle. 这个 handle 运行这个请求,通过一个filter chain,这个 filter chain在请求路由前后都能执行。
五、gateway的限流
gateway作为网关,与其他网关技术不同的是它能实现限流。gateway使用的是令牌桶算法实现限流。常见的限流算法有:
1、计数器算法:以QPS为100举例,如果1秒钟内钱200ms请求数量到达了100,后面800ms中的请求都会被拒绝,这种情况称为”突刺现象“
2、漏桶算法:可以解决突刺现象。比如创建一个很大的队列来接收请求,一个较小的线程池来处理请求。但是也有极限情况,当队列满了时, 请求也会拒绝掉。
3、令牌桶算法:可以说是漏桶算法的改进。在桶中放令牌,请求获取令牌后才能继续执行。如果桶中无令牌,请求可以选择进行等待或直接拒绝。
在项目中使用gateway网关做限流一般结合的redis,使用令牌桶算法。
六、gateway的断言
gateway包含了许多内置的路由断言工厂。所有的这些断言匹配不同的HTTP请求属性。你能组合这些路由断言工厂。我们一般是在配置文件中配置predicates,当然我们也可以自定义Predicate,如下:
@Componentpublic class CustomeRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> { public CustomeRoutePredicateFactory() { super(PathRoutePredicateFactory.Config.class); } @Override public Predicate<ServerWebExchange> apply(PathRoutePredicateFactory.Config config) { System.out.println("TokenRoutePredicateFactory Start..."); return exchange -> { ServerHttpRequest request = exchange.getRequest(); //take information from the request to see if it //matches configuration. return matches(config, request); }; } private boolean matches(PathRoutePredicateFactory.Config config, ServerHttpRequest request) { System.out.println(request.getBody()); RequestPath path = request.getPath(); if(path.toString().contains("gateway")) return false; return true; }}ymlroutes: - id: path_route uri: lb://PROVIDER # lb的意思是负载均衡 predicates: - Custome=/gateway/** #这个Custome就是自定义类CustomeRoutePredicateFactory的前缀,仿照PathRoutePredicateFactory写的 filters: - name: Hystrix #表示filter中使用Hystrix来做熔断降级 args: name: fallbackcmd #名字唯一即可 fallbackUri: forward:/defaultfallback #在本gateway中产生的异常或超时将调用本gateway中的control层中的defaultfallback
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
上面的逻辑是,获取路径值,如果存在gateway字符串,则返回false。
gateway给我们提供了很多内置的Predicate,如断言时间的 AfterRoutePredicateFactory、BeforeRoutePredicateFactory…,断言cookie的CookieRoutePredicateFactory,断言请求头的HeaderRoutePredicateFactory,一般见其名知其意,且yml中配置时只需要写前缀即可,且一般有两个参数,第一个值一般都是Java的正则表达式。例如
spring: cloud: gateway: routes: - id: cookie_route uri: https://example.org predicates: - Cookie=chocolate, ch.p #Cookie就是前缀名
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
七、gateway的filter
gateway的filter有三种:全局Filter、默认Filter和自定义Filter。
全局Filter一般是我们定义,全局的filter一般要实现GlobalFilter 和Orderd,我们可以设置getOrder()方法来设置当前Filter的执行权重。order值越小,请求服务前的优先级越高,服务返回后执行的优先级越低。
如下
@Bean public GlobalFilter customGlobalFilter() { return (exchange, chain) -> exchange.getPrincipal() .map(Principal::getName) .defaultIfEmpty("Default User") .map(userName -> { //adds header to proxied request exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build(); return exchange; }) .flatMap(chain::filter); } @Bean public GlobalFilter customGlobalPostFilter() { return (exchange, chain) -> chain.filter(exchange) .then(Mono.just(exchange)) .map(serverWebExchange -> { //adds header to response serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER", HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work"); return serverWebExchange; }) .then(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
gateway也为我们提供了很多的内置Filter,即默认的Filter,像AddRequestHeaderGatewayFilter,AddRequestParameterGatewayFilter等等。在yml中配置也只需要写前缀即可,如下
spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org filters: - AddRequestParameter=red, blue
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
下面讲几个常用的gateway内置的Filter:ForwardRoutingFilter和ReactiveLoadBalancerClientFilter
ForwardRoutingFilter:这个过滤器将URI视为是可改变的属性。当URL有forward修饰时,例如:forward://localendpoint,它会使用spring的DispatcherHandle来处理请求,换句话说,就是用DispatcherHandle来处理forward请求转发。
ReactiveLoadBalancerClientFilter:就是用来负载均衡的。当URL有lb时,例如:lb://myservice,它使用springCloud的ReactLoadBalancer去解析服务名为myservice的微服务(将这个myservice解析为实际的主机和端口,并替换当前的URI),找到这个微服务的集群,并负载均衡到某一台,执行服务。
spring: cloud: gateway: routes: - id: myRoute uri: lb://service predicates: - Path=/service/**
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
注意:gateway支持所有的loadbalancer特性。
八、gateway的降级熔断
先看看官网的例子,如下
spring: cloud: gateway: routes: - id: circuitbreaker_route uri: lb://backing-service:8088 predicates: - Path=/consumingServiceEndpoint filters: - name: CircuitBreaker args: name: myCircuitBreaker fallbackUri: forward:/inCaseOfFailureUseThis - RewritePath=/consumingServiceEndpoint, /backingServiceEndpoint
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
表示如果发生了fallback,则到fallbackUri地址中的controller。正常的话,是到uri中的controller
当然,你也可以使用配置的方式,如下
@Beanpublic RouteLocator routes(RouteLocatorBuilder builder) { return builder.routes() .route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint") .filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis")) .rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088") .build();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
熔断器也可以这样写,没有fallback或处理在gateway应用中,而是有一个注册在主机9994端口下的应用,这个应用可以处理fallback。
spring: cloud: gateway: routes: - id: ingredients uri: lb://ingredients predicates: - Path=//ingredients/** filters: - name: CircuitBreaker args: name: fetchIngredients fallbackUri: forward:/fallback - id: ingredients-fallback uri: http://localhost:9994 predicates: - Path=/fallback
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
下面看看我们自己设置的熔断降级,如下
//在当前gateway中的controller,如果Yml中没有配置其他主机的fallback方法,如果配置了,则该方法配置在另外的主机上。@Configurationpublic class CircuitConfig { @HystrixCommand(commandKey = "gatewayCommand",fallbackMethod = "gatewayFallback",commandProperties = { @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED,value = "true"), @HystrixProperty(name =HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value = "10000"), @HystrixProperty(name =HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value = "40"), @HystrixProperty(name =HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value = "5"), }) public String gatewayCommand(){ return "gatewayCommand method.."; } public String gatewayFallback(){ return "gatewayFallback method.."; }gateway的yml routes: - id: path_route uri: lb://PROVIDER # lb的意思是负载均衡 predicates: - Custome=/gateway/** filters: - name: Hystrix args: name: fallbackcmd fallbackUri: forward:/defaultfallback
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
注意,当前fallbackuri只能使用forward!如果没配置其他的处理fallback的主机,则重定向到当前网关主机的controller路径。
九、gateway整合actuator
需要actuator的依赖。
一般gateway的路由信息是配置注册中心,我们可以使用原始的手工方式刷新路由缓存。使用post请求/actuator/gateway/refresh 即可。当然这需要导入spring的actuator依赖
你也可以检索gateway中所有的路由信息,以手工方式。使用get请求/actuator/gateway/routes
你也可以检索某一个路由信息,使用get请求/actuator/gateway/routes/{id}
总之,可以使用actuator来CRUD路由。
十、总结
1、gateway网关作为整个项目的最上层,其实就是对请求的处理,细化说有:请求路由转发,限流,熔断降级和负载均衡等。但是在项目中一般使用的请求转发和负载均衡。
2、gateway中限流使用的是令牌桶算法。
3、gateway的熔断降级只能在本网关内出现的异常和超时,如果正常请求到别的微服务,而在别的微服务中出现的超时和异常,配置在gateway网关中的熔断降级不起效。
4、gateway使用的异步非阻塞的模型,底层使用的Netty框架,可和springwebFlux、springMvc结合使用。
5、gateway中的Fileter可以理解为是spring中的Aop机制中的环绕通知。有前置和后置,针对请求服务前后而言。Filter的order值越小,执行前置的优先级越大,执行后置的优先级越小。