SpringCloud Gateway
是什么?
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。
是所有微服务访问的统一入口
图片来源自网络
做什么的
Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等
图片来源自网络
为什么要用
SpringCloud官方出品 方便使用,Zuul2 并没有整合进SrpingCloud中
特征
- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
- 动态路由
- Predicates 和 Filters 作用于特定路由
- 集成 Spring Cloud DiscoveryClient
- 易于编写的 Predicates 和 Filters
- 限流
- 路径重写
- 集成断路器hystrix(gateway3.0+已经删除)
关键名词及作用
-
路由:网关的基本构建组成,它由ID,目标URL,谓词集合和过滤器定义,如果聚合谓词为true,则匹配路由;
-
谓词:符合Predicate的条件,就使用该路由的配置,否则就不管,Predicate是Java 8提供的一个函数式编程接口。
-
过滤器:用于发送和返回请求之前,修改请求和响应
扫描二维码关注公众号,回复: 13470746 查看本文章
关于过滤器
过滤器分为“pre”和“post”两种方式
-
pre:在请求被路由之前调用,可以做参数校验、权限校验、流量监控、日志输出、协议转换等
-
post:在路由到微服务以后执行,可以做响应内容、响应头的修改,日志的输出,流量监控等
Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter(局部过滤器) 与 GlobalFilter(全局过滤器)。
-
GatewayFilter :应用到单个路由或者一个分组的路由上。
-
GlobalFilter :应用到所有的路由上。
图片来源自网络
开始使用
该文章使用3.0+版本
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
复制代码
不要导入web依赖(下面的依赖),gateway的starter中已经引入了spring-boot-starter-webflux依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
复制代码
否则启动项目时会出现 Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway。(在类路径上发现Spring MVC,它与Spring Cloud Gateway不兼容。)
谓词使用
Cookie 路由谓词工厂
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: cookie_route
uri: http://www.baidu.com 需要跳转的地址
predicates:
- Cookie=ityouknow, kee.e
复制代码
postman调用截图
图片来源自网络
请求头路由谓词工厂
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: head_route
uri: http://www.baidu.com
predicates:
- Header=head, \d+ # \d+ 只能是数字 + 号表示至少出现一次
复制代码
postman调用截图
path路由谓词工厂
另起一个端口为8087的服务
@RestController
@RequestMapping("/tool")
public class ToolController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}
复制代码
配置信息
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: path_route
uri: http://localhost:8087
predicates:
- Path=/tool/** # 只要包含tool就会匹配上
# - Path=/tool/{segment} 包含tool再往后匹配一个 斜杠区间 比如 localhost:9000/tool/hello 能匹配上 localhost:9000/tool/hello/v1 不能匹配上(修改8087项目方法地址为/hello/v1再做测试)
复制代码
postman调用
postman请求 localhost:9000/tool/hello
返回hello
请求参数路由谓词工厂
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: query_route
uri: http://localhost:8087
predicates:
- Query=val,0?(13|14|15|17|18|19)[0-9]{9}
# - Query=val 匹配参数中包含val的请求
# - Query=val,0?(13|14|15|17|18|19)[0-9]{9} 匹配参数中包含val,并且参数值需要符合逗号后面的正则表达式 的请求,
# 该表达式是手机号的正则,测试时可以试试其他的,正则表达式总长度不能大于50,否则会报错
复制代码
修改代码
@RestController
@RequestMapping("/tool")
public class ToolController {
@GetMapping("/hello")
public String hello(String val){
return val;
}
}
复制代码
postman调用
postman请求 localhost:9000/tool/hello?val=1531xxxxxxx 返回参数的值
以上就是常用谓词的简单用例了
过滤器使用
1、添加请求头过滤器工厂 AddRequestHeader GatewayFilter factory
添加接口
@RestController
@RequestMapping("request")
public class RequestController {
@GetMapping("head")
public void getHead(HttpServletRequest request){
System.out.println(request.getHeader("X-Request-Tool"));
}
}
复制代码
添加过滤器配置
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: add_header_route
uri: http://localhost:8087
predicates:
- Path=/request/**
filters:
- AddRequestHeader=X-Request-Tool, Box
复制代码
访问 localhost:9000/request/head
服务端打印结果
2、删除请求头过滤器工厂 RemoveRequestHeader
添加过滤器配置
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: rm_header_route
uri: http://localhost:8087
predicates:
- Path=/request/**
filters:
- RemoveRequestHeader=X-Request-Tool
复制代码
postman请求截图
服务端打印结果
3、添加参数工厂
添加接口
@GetMapping("parameter")
public void getParameter(HttpServletRequest request){
System.out.println(request.getParameter("red"));
}
复制代码
修改配置信息
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: parameter_route
uri: http://localhost:8087
predicates:
- Path=/request/**
filters:
- AddRequestParameter=red, blue
# 添加 名为red,值为bule 的参数
复制代码
请求 localhost:9000/request/parameter
控制台打印结果
过滤器工厂基本使用就列举这三个,其他的可以去SpringCloud Gateway官网查看。
4、自定义过滤器的实现
1)全局过滤器的自定义实现 GlobalFilter
直接实现 GlobalFilter接口即可,不用添加任何配置,直接作用在所有请求,所有路由
@Component
public class LoginFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取请求头中的token 如果没有则直接结束
String token = exchange.getRequest().getHeaders().getFirst("token");
if(ObjectUtils.isEmpty(token)){
// 3.如果不存在 : 认证失败
System.out.println("没有登录");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete(); // 请求结束
}
// 4.如果存在,继续执行
return chain.filter(exchange); // 继续向下执行
}
@Override
public int getOrder() {
// 优先级 数值越大执行越靠后
return 100;
}
}
复制代码
postman调用截图
2) 局部过滤器 GatewayFilter
继承AbstractGatewayFilterFactory,需要配置作用的某个(/a/b)或者某类(/a/*)路由信息
提醒:
predicates:
- Path=/**/**
## 这样的配置信息会在启动项目时报错
复制代码
实现代码
@Component
public class TogetherFilter extends AbstractGatewayFilterFactory {
@Override
public GatewayFilter apply(Object config) {
String name = "";
return (exchange,chain) -> {
//获取URI
URI newUri = exchange.getRequest().getURI();
String rawPath = newUri.getRawPath();
System.out.println(rawPath);
rawPath = rawPath + "/url";
//修改URI
ServerHttpRequest build = exchange.getRequest().mutate().path(rawPath).build();
return chain.filter(exchange.mutate().request(build).build());
};
}
}
复制代码
将自定义的过滤器配置到yml中
server:
port: 9000 # 指定网关服务的端口
spring:
application:
name: api-gateway
cloud:
gateway:
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: together_route
uri: http://localhost:8087
predicates:
- Path=/request
filters:
- TogetherFilter
复制代码
添加接口
@GetMapping("url")
public String getUrl(HttpServletRequest request){
return request.getRequestURI();
}
复制代码
postman请求结果
如果需要多个过滤器取满足多组路由信息,需要在yml中配置多个
routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
- id: cookie_route1
uri: http://localhost:8087
predicates:
- Path=/tool
filters:
- LogFilter2
------------------------------------
- id: cookie_route2
uri: http://localhost:8087
predicates:
- Path=/request
filters:
- LogFilter3
复制代码
完!