目录
一、引子
2年前有幸使用过一次Spring Cloud (1.5.9),1.* 集成的是ZUUL做网关。终于在2年后,这次果断使用Spring Cloud Gateway。
区别:
Zuul构建于 Servlet 2.5,兼容 3.x,使用的是阻塞式的 API,不支持长连接,比如 websockets。
Spring Cloud Gateway构建于 Spring 5+,基于 Spring Boot 2.x 响应式的、非阻塞式的 API。支持 websockets,和Spring 框架紧密集成。底层使用netty模型,性能极高。
备注:
spring cloud 已放弃zuul 2.0,使用了自己的亲儿子gateway.后续估计也不会再集成2.0了,建议从zuul转向gateway.
一个简单的创业项目架构图如下:

二、Gateway设计思想
2.1 官网设计
自从撇开netflex zuul后,spring Cloud速度搜搜的。我开发时还是用2.1.4,目前最新已经到2.2.1,附上官网飞机票
2.1.1 特性
-
Built on Spring Framework 5, Project Reactor and Spring Boot 2.0:基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
-
Able to match routes on any request attribute.:能匹配任意请求属性的路由
-
Predicates and filters are specific to routes.:针对特定路由使用匹配策略和过滤器
-
Hystrix Circuit Breaker integration. :集成Hystri断路器
-
Spring Cloud DiscoveryClient integration:集成服务发现(gateway一样可注册到eureka)
-
Easy to write Predicates and Filters:易于写策略(断言)+过滤器
-
Request Rate Limiting 请求限流
-
Path Rewriting:重写path
简单来说就是Route、Predicate、Filter三大核心组件。
2.1.2 流程图
如上图,Gateway Client客户端发送请求在Gateway Handler Mapping中查找是否命中路由策略,命中的话请求转发给Gateway Web Handler来处理。根据定义的多个Filter链,执行顺序:Pre Filter->代理请求->Post Filter。
2.1.3 内置Predicates+Filter
Gateway内置了11个Predicates Factories路由策略(断言)工厂类。
Filter分2类:
- 31个GatewayFilter Factories网关过滤器工厂类
- 10个GlobalFilter 全局过滤器接口
这里就不在过多介绍,建议有需求时可以去官网找找,没有的话再自己开发。
2.2 我们的使用
1.使用Route结合Hystrix实现默认降级策略
2.使用GatewayFilter接口,自定义过滤器类,实现登录态(token)校验
三、Gateway简单使用
3.1 实现微服务的默认降级策略
| 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 |
|
如上图,我们开启了2个微服务route路由。
- 1)前端请求时path带/gateway/,在gateway层使用StripPrefix=1,去掉gateway,最终微服务上的path不带"/gateway/".
- 2)使用Hystrix实现默认降级策略,降级接口实现如下:
| 1 2 3 4 5 6 7 8 9 10 11 |
|
3.2 实现登录态(token)校验
3.2.1 自定义过滤器
自定义过滤器,实现GatewayFilter, Ordered 2个接口。
1 import com.*.auth.UserTokenTools;
2 import lombok.extern.slf4j.Slf4j;
3 import org.apache.commons.lang3.StringUtils;
4 import org.springframework.cloud.gateway.filter.GatewayFilter;
5 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
6 import org.springframework.core.Ordered;
7 import org.springframework.http.HttpHeaders;
8 import org.springframework.http.HttpStatus;
9 import org.springframework.http.server.reactive.ServerHttpRequest;
10 import org.springframework.http.server.reactive.ServerHttpResponse;
11 import org.springframework.stereotype.Component;
12 import org.springframework.web.server.ServerWebExchange;
13 import reactor.core.publisher.Mono;
14
15 /**
16 * @author denny.zhang
17 * @Description token过滤器
18 * @date 2019/12/12 13:55
19 */
20 @Slf4j
21 @Component
22 public class LoginTokenFilter implements GatewayFilter, Ordered {
23
24 private static final String AUTHORIZE_TOKEN = "Authorization";
25 private static final String BEARER = "Bearer ";
26
27 /**
28 * token过滤
29 *
30 * @param exchange
31 * @param chain
32 * @return
33 */
34 @Override
35 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
36 log.info("当前环境已开启token校验");
37 ServerHttpRequest request = exchange.getRequest();
38 HttpHeaders headers = request.getHeaders();
39 ServerHttpResponse response = exchange.getResponse();
40 // 取Authorization
41 String tokenHeader = headers.getFirst(AUTHORIZE_TOKEN);
42 log.info("tokenHeader=" + tokenHeader);
43 // token不存在
44 if (StringUtils.isEmpty(tokenHeader)) {
45 response.setStatusCode(HttpStatus.UNAUTHORIZED);
46 return response.setComplete();
47 }
48 // 取token
49 String token = this.getToken(tokenHeader);
50 log.info("token=" + token);
51
52 // token不存在
53 if (StringUtils.isEmpty(token)) {
54 log.info("token不存在");
55 response.setStatusCode(HttpStatus.UNAUTHORIZED);
56 return response.setComplete();
57 }
58 // 校验 token是否失效
59 if (UserTokenTools.isTokenExpired(token, null)) {
60 log.info("token失效");
61 response.setStatusCode(HttpStatus.UNAUTHORIZED);
62 return response.setComplete();
63 }
64 // 校验 token是否正确
65 if (!UserTokenTools.checkToken(token, null)) {
66 response.setStatusCode(HttpStatus.UNAUTHORIZED);
67 return response.setComplete();
68 }
69
70 // //有token 这里可根据具体情况,看是否需要在gateway直接把解析出来的用户信息塞进请求中,我们最终没有使用
71 // UserTokenInfo userTokenInfo = UserTokenTools.getUserTokenInfo(token);
72 // log.info("token={},userTokenInfo={}",token,userTokenInfo);
73 // request.getQueryParams().add("token",token);
74 //request.getHeaders().set("token", token);
75 return chain.filter(exchange);
76 }
77
78
79 @Override
80 public int getOrder() {
81 return -10;
82 }
83
84 /**
85 * 解析Token
86 */
87 public String getToken(String requestHeader) {
88 //2.Cookie中没有从header中获取
89 if (requestHeader != null && requestHeader.startsWith(BEARER)) {
90 return requestHeader.substring(7);
91 }
92 return "";
93 }
94 }
上图中,UserTokenTools是我们自定义的一个JWT工具类,用来生成token,校验token过期、正确等。
3.2.2 配置路由
大家可根据具体情况,如果只有一套登录态,那就用一个filter即可。
1 import com.*.gateway.filter.AuthorizeGatewayFilter;
2 import com.*.gateway.filter.LoginTokenFilter;
3 import org.springframework.cloud.gateway.route.RouteLocator;
4 import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
5 import org.springframework.context.annotation.Bean;
6 import org.springframework.context.annotation.Configuration;
7
8
9 @Configuration
10 public class GatewayConfig {
11
12 @Bean
13 public RouteLocator getRouteLocator(RouteLocatorBuilder builder) {
14 return builder.routes()
15 // token校验1
16 .route(predicateSpec -> predicateSpec
17 .path("/gateway/pay/card/**", "/gateway/app/**")
18 .filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new AuthorizeGatewayFilter()))
19 .uri("lb://OLOAN-PAY-SERVICE")
20 .id("OLOAN-PAY-SERVICE-token"))
21
22 // token校验2
23 .route(predicateSpec -> predicateSpec
24 .path("/gateway/order-audit/**", "/gateway/order/**", "/gateway/order-payment/**")
25 .filters(gatewayFilterSpec -> gatewayFilterSpec.stripPrefix(1).filter(new LoginTokenFilter()))
26 .uri("lb://OLOAN-ORDER-SERVICE")
27 .id("OLOAN-ORDER-ORDER-token"))
28 .build();
29 }
30 }
四、总结
4.1.WebFlux
Spring Cloud Gateway使用WebFlux,和spring boot web包冲突,使用时一定记得pom中排除原来老WEB那一套(servlet)相关jar,否则会报错。
| 1 2 3 4 5 6 7 8 9 10 |
|
4.2.Gateway Filter
Gateway Filter 自带的源码支撑错误码response.setStatusCode(HttpStatus.UNAUTHORIZED);并不是那么的友好。错误码枚举使用的是spring自带框架的枚举类:
org.springframework.http.HttpStatus:
UNAUTHORIZED(401, "Unauthorized")
这样请求返回的结构体和一般定义的JSON格式(code message data)不同。当然官方也是提供了解决方案。后续再去优化吧。
4.3 限流
gateway默认实现了几个简单的限流策略(依赖redis),后续可以使用一下。
本文深入探讨SpringCloud Gateway的使用,对比Zuul,讲解Gateway的设计思想、特性及流程,包括默认降级策略、登录态校验及限流的实现。




1328

被折叠的 条评论
为什么被折叠?



