Spring Cloud Gateway 是 Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul
。相比 Zuul
来说,Spring Cloud Gateway 提供更优秀的性能,更强大的有功能。
配置网关, Spring Cloud Gateway是由 WebFlux
+ Netty
+ Reactor
实现的响应式的 API
网关。它不能在传统的 servlet
容器中工作,也不能构建成 war
包。
Gateway 的核心概念如下:
route
) : predicates
) : Filter
) Gateway FilIer
和Global Filter
。Filter可以对请求和响应进行处理。 路由route
是想要访问的目标地址,断言predicates
是匹配请求中的一些信息,满足条件才允许访问目标地址,而Gateway FilIer
网关过滤器可以为请求做某些扩展,比如添加一些请求头、请求参数等等,但Gateway FilIer
只能作用于对应路由下的请求!
而GlobalFilter
全局过滤器和 GatewayFilter
网关过滤器虽然有一样的接口定义,只不过, GlobalFilter
会作用于所有路由。
网关怎么设置才能上网、
Gateway 的工作原理如下:
其实Gateway
对请求的处理与spring mvc
的处理差不多!也是一套流程下来的
框架 | Gateway | spring mvc |
---|---|---|
请求分发 | DispatcherHandler | DispatcherServlet |
请求映射 | HandlerMapping | HandlerMapping |
请求适配 | HanderAdaper | HanderAdaper |
请求处理 | WebHander | Hander |
Spring Cloud Gateway是Spring Cloud 的一个子项目。而zuul
则是netflix
公司的项目,只是spring将zuul集成在 Spring Cloud 中使用而已。因为zuul2.0
连续跳票和zuul1.0
的性能表现不是很理想,所以催生了spring团队开发了Gateway
项目。
网关地址一般是多少,Zuul:
websockets
。servlet
,Zuul处理的是http
请求hystrix
支持。spring-cloud-starter-netflix-zuul
。Gateway:
Netty
底层环境,不能和传统的Servlet
容器一起使用,也不能打包成一个war
包。spring-boot-starter-webflux
和 spring-cloud-starter-gateway
RedisRateLimiter
。相同点:
servlet
web
网关不同点:
win10以太网没有ip配置、适用性
是否支持异步
zuul
仅支持同步gateway
支持异步。理论上gateway
则更适合于提高系统吞吐量(但不一定能有更好的性能),最终性能还需要通过严密的压测来决定框架设计的角度
性能
gateway
是基于spring-webflux的响应式、非阻塞编程,且支持异步,底层通过netty
通信,性能较高。与Spring紧密集成,所以将会是一个更好的开发体验。Zuul 1.x
,是一个基于阻塞io的API Gateway。Zuul已经发布了Zuul 2.x
,基于Netty,也是非阻塞的,支持长连接,但Spring Cloud暂时还没有整合计划。没有有效的ip配置,
Gateway
不能在传统的 servlet 容器中工作,也不能构建成 war 包,会和spring-webmvc
的依赖冲突,所以不能引入 web
环境依赖 <properties><java.version>1.8</java.version><spring-cloud.version>Hoxton.SR8</spring-cloud.version><spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.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><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- gateway网关 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!-- nacos服务注册与发现 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
yml
配置文件spring:application:name: mall-gateway# 配置nacos注册中心地址cloud:nacos:discovery:server-addr: 127.0.0.1:8848gateway:discovery:locator:# 默认为false,设为true开启通过微服务创建路由的功能,即可以通过微服务名访问服务# http://localhost:8888/mall-order/order/findOrderByUserId/1enabled: false# 是否开启网关enabled: true# 网关配置跨域globalcors:cors-configurations:'[/**]':allowedOrigins: "*"allowedMethods:- GET- POST- DELETE- PUT- OPTION# 设置路由:路由id、路由到微服务的uri、断言routes:- id: order_route #路由ID,全局唯一,建议配合服务名
# # uri: http://localhost:8020 #目标微服务的请求地址和端口uri: lb://mall-order #lb 整合负载均衡器ribbon,loadbalancerpredicates:
# # Path路径匹配- Path=/order/**# 测试 http://localhost:8888/order/findOrderByUserId/1# 匹配在指定的日期时间之后发生的请求 入参是ZonedDateTime类型- After=2021-05-16T20:50:57.511+08:00[Asia/Shanghai]# Cookie匹配- Cookie=username, fox# Header匹配 请求中带有请求头名为 x-request-id,其值与 \d+ 正则表达式匹配- Header=X-Request-Id, \d+#自定义CheckAuth断言工厂- name: CheckAuthargs:name: fox- CheckAuth=abc#配置过滤器工厂filters:- AddRequestHeader=X-Request-color, red #添加请求头- AddRequestParameter=color, blue # 添加请求参数- PrefixPath=/mall-order # 添加前缀 对应微服务需要配置context-path#- RedirectTo=302, http://baidu.com #重定向到百度- CheckAuth=aaa,男 #配置自定义的过滤器工厂- id: user_routeuri: lb://mall-user #lb 整合负载均衡器ribbon,loadbalancerpredicates:- Path=/user/**
启动应用!配置即可生效
更多路由断言工厂请参考spring官网 https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories,下面仅列出几种常用的断言类型!
设置网关?时间匹配:可以用在限时抢购的一些场景中。
# 匹配在指定的日期时间之后发生的请求 入参是ZonedDateTime类型 - After=2021-01-31T22:22:07.783+08:00[Asia/Shanghai]
其中 获取ZonedDateTime
类型的指定日期时间代码如下:
ZonedDateTime zonedDateTime = ZonedDateTime.now();//默认时区
// 用指定时区获取当前时间
ZonedDateTime zonedDateTime2 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
Cookie匹配:请求头必须加上 Cookie :aaa,否则请求失败!
# Cookie匹配
- Cookie=username, aaa
Header匹配:请求头必须加上 X-Request-Id:2(整数),否则请求失败!
# Header匹配 请求中带有请求头名为 x-request-id,其值与 \d+ 正则表达式匹配 - Header=X-Request-Id, \d+
路径匹配:只有是 order/** 的请求,才能通过断言
# 测试:http://localhost:8888/order/findOrderByUserId/1
- Path=/order/** #Path路径匹配
自定义路由断言工厂
上面所有的断言配置都继承自AbstractPredicateFactory
抽象工厂,如下所示:
所以当我们有个性化需求需要自定义自定义路由断言工厂时,也可参照上述配置进行定制,需要继承 AbstractRoutePredicateFactory
类,重写 apply
方法的逻辑。在 apply
方法中可以通过 exchange.getRequest()
拿到 ServerHttpRequest
对象,从而可以获取到请求的参数、请求方式、请求头等信息。
注意: 命名需要以 RoutePredicateFactory
结尾
自定义断言工厂:判断参数name
是否是aaa
!
/*** 自定义RoutePredicateFactory*/
@Component
@Slf4j
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {public CheckAuthRoutePredicateFactory() {super(Config.class);}@Overridepublic Predicate<ServerWebExchange> apply(Config config) {return new GatewayPredicate() {@Overridepublic boolean test(ServerWebExchange serverWebExchange) {log.info("调用CheckAuthRoutePredicateFactory" + config.getName());if(config.getName().equals("aaa")){return true;}return false;}};}/*** 快捷配置* @return*/@Overridepublic List<String> shortcutFieldOrder() {return Collections.singletonList("name");}/*** 需要定义一个内部类,该类用于封装application.yml中的配置*/public static class Config {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}
}
yml
中配置
predicates:- Path=/order/**# 自定义CheckAuth断言工厂- name: CheckAuthargs:name: aaa# 自定义CheckAuth断言工厂快捷配置! 二选一# - CheckAuth=aaa
SpringCloudGateway
内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加或去除参数等
添加请求头
filters:
- AddRequestHeader=X-Request-color, red #添加请求头
添加请求参数
filters:
- AddRequestParameter=color, blue # 添加请求参数
为匹配的路由统一添加前缀
filters:
- PrefixPath=/mall-order # 添加前缀 比如某个微服务需要配置context-path
重定向操作
filters:
- RedirectTo=302, http://baidu.com #重定向到百度
自定义过滤器工厂
与自定义断言工厂类似,自定义过滤器工厂需要继承AbstractNameValueGatewayFilterFactory
抽象类,且自定义类名必须要以GatewayFilterFactory
结尾并交给spring
管理。重写apply
方法后如下:
自定义过滤器工厂:为当前路由下的请求添加参数name
、sex
参数,参数值为 aaa
,男
!
@Component
@Slf4j
public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {@Overridepublic GatewayFilter apply(NameValueConfig config) {return (exchange, chain) -> {log.info("调用CheckAuthGatewayFilterFactory==="+ config.getName() + ":" + config.getValue());// TODOreturn chain.filter(exchange);};}
}
yml
配置
spring:cloud:gateway:routes:- id: order_route #路由ID,全局唯一,建议配合服务名uri: lb://mall-order #lb 整合负载均衡器ribbon,loadbalancerfilters:- CheckAuth=aaa,男 #配置自定义的过滤器工厂
GlobalFilter
接口和 GatewayFilter
有一样的接口定义,只不过 GlobalFilter
会作用于所有路由。比如:LoadBalancerClientFilter
负载均衡过滤器 lb://mall-order
。自定义的全局过滤器需要实现GlobalFilter
接口,并重写filter
方法!
注意:全局过滤器(Global Filters
)不需要在yml
中配置!!
官方声明:GlobalFilter的接口定义以及用法在未来的版本可能会发生变化。
自定义校验token的全局过滤器
@Component
@Order(-1)
@Slf4j
public class CheckAuthFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//校验请求头中的tokenList<String> token = exchange.getRequest().getHeaders().get("token");log.info("token:"+ token);if (token.isEmpty()){return chain.filter(exchange);}// TODO token校验return chain.filter(exchange);}
}
自定义IP
全局过滤器,黑白名单
@Component
@Slf4j
public class CheckIPFilter implements GlobalFilter, Ordered {// 执行顺序控制!@Overridepublic int getOrder() {return 0;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {HttpHeaders headers = exchange.getRequest().getHeaders();//模拟对 IP 的访问限制,即不在 IP 白名单中就不能调用的需求if (getIp(headers).equals("127.0.0.1")) {log.info("======非法访问======");ServerHttpResponse response = exchange.getResponse();byte[] bytes = new String("======非法访问======").getBytes();response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);DataBuffer buffer = response.bufferFactory().wrap(bytes);response.getHeaders().add("Content-Type","application/json;charset=UTF-8");return exchange.getResponse().writeWith(Mono.just(buffer));}return chain.filter(exchange);}private String getIp(HttpHeaders headers) {return headers.getHost().getHostName();}
}
测试结果: 如果请求ip
为127.0.0.1
则不允许访问!如果是localhost
就可以访问!
为了保证 Gateway
的高可用性,可以同时启动多个 Gateway
实例进行负载,在 Gateway
的上游使用 Nginx
进行负载转发以达到高可用。
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态