Spring Cloud Gateway是基于Spring Boot 2.x,Spring WebFlux和Project Reactor 构建的。属于异步非阻塞架构
Spring Cloud Gateway与Spring Data 和Spring Securit 技术不能同时使用
Spring Cloud Gateway基于Spring Boot和Spring Webflux提供的Netty运行。它在传统的Servlet容器中或用WAR的方式构建时不起作用
网关基本的功能 :鉴权、流量控制、熔断、路径重写、⽇志监控等
GateWay核心概念
-
路由(route): 网关最基础的部分,也是网关比较基础的工作单元。
路由由一个ID、一个目标 URL(最终路由到的地址)、一系列的断言(匹配条件判断)和Filter过滤器(精细化控制)组成。
如果断言为true,则匹配该路由。 -
断言(predicates):参考了Java8中的断言java.util.function.Predicate
开发人员可以匹配Http 请求中的所有内容(包括请求头、请求参数等)(类似于Nginx中的location匹配一样)
如果断言与请求相匹配则路由。 -
过滤器(filter):⼀个标准的Spring webFilter,使用过滤器,可以在请求之前或者之后执行业务逻辑。
GateWay工作过程
解释:
客户端向Spring Cloud GateWay发出请求,然后在GateWay Handler Mapping中找到与请求相匹配的路由,将其发送到GateWay Web Handler;
Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(pre)或者之后(post)执行业务逻辑。
Filter在“pre”类型过滤器中可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
在“post”类型的过滤器中可以做响应内容、响应头的修改、日志的输出、流量监控等。
GateWay应用
GateWay不需要使用web模块,它引入的是WebFlux(类似于SpringMVC)
<!--GateWay ⽹关-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--引⼊webflux-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
application.yml
server:
port: 88
spring:
application:
name: payment-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
routes: # 路由可以有多个
##产品服务路由器,拦截/api/product请求,并重写url地址
- id: product_route # ⾃定义的路由 ID,保持唯⼀
uri: lb://payment-product # ⽬标服务地址 lb表是负载均衡,payment-product 服务名
predicates: # 断言
- Path=/api/product/**
filters: # 过滤器
- RewritePath=/api/(?<segment>/?.*), /$\{segment}
GateWay断言详解
Spring Cloud GateWay 帮我们内置了很多 Predicates功能,实现了各种路由匹配规则
时间点后匹配 :
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
时间点前匹配 :
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
时间区间匹配 :
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
指定Cookie正则匹配指定值 :
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://example.org
predicates:
- Cookie=chocolate, ch.p
指定Header正则匹配指定值 :
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://example.org
predicates:
- Header=X-Request-Id, \d+
请求Host匹配指定值 :
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
请求Method匹配指定请求方式 :
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://example.org
predicates:
- Method=GET,POST
请求路径正则匹配 :
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://example.org
predicates:
- Path=/red/{segment},/blue/{segment}
请求包含某参数 :
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://example.org
predicates:
- Query=green
远程地址匹配 :
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
权重规则匹配:
spring:
cloud:
gateway:
routes:
- id: weight_high
uri: https://weighthigh.org
predicates:
- Weight=group1, 8
- id: weight_low
uri: https://weightlow.org
predicates:
- Weight=group1, 2
该路由会将约80%的流量转发至https://weighthigh.org并将约20%的流量转发至https://weighlow.org/
GateWay过滤器
Header头部过滤器:
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue
该过滤器将会把X-Request-red:blue
添加到请求头信息中。
Parameter请求参数过滤器:
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=red, blue
该过滤器将会添加red=blue
参数到所有匹配下游请求中。
Path路径过滤器:
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/red/**
filters:
- RewritePath=/red(?<segment>/?.*), $\{segment}
请求路径/red/blue
,将会被修改为/blue
。
重试过滤器:
spring:
cloud:
gateway:
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
methods: GET,POST
backoff:
firstBackoff: 10ms
maxBackoff: 50ms
factor: 2
basedOnPreviousValue: false
参数详解:
-
retries
:重试次数,默认值是3次 -
series
:状态码,符合的某段状态码才会进行重试逻辑,默认值是SERVER_ERROR,值是5,也就是5XX(5开头的状态码),共有5个值:
public enum Series {
INFORMATIONAL(1),
SUCCESSFUL(2),
REDIRECTION(3),
CLIENT_ERROR(4),
SERVER_ERROR(5);
}
methods
:指定哪些方法的请求需要进行重试逻辑,默认值是GET方法,取值如下
public enum HttpMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
}
exceptions
:指定哪些异常需要进行重试逻辑,默认值是java.io.IOException
和TimeoutException
Hystrix过滤器:
内置了 HystrixGatewayFilterFactory ,只需要配置即可实现熔断回退功能
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingserviceendpoint
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/fallback
- RewritePath=/consumingserviceendpoint, /backingserviceendpoint
@RequestMapping("/fallback")
public Object fallback(){
return "降级";
}
注意:项目需要引入Hystrix依赖
fallbackUri 是发生熔断时回退的 URI 地址,目前只支持 forward 模式。如果服务被降级,该请求会被转发到该 URI 中
自定义过滤器:
案例:自定义全局过滤器实现IP访问限制(黑白名单)
请求过来时,判断发送请求的客户端的ip,如果在黑名单中,拒绝访问
/**
* 定义全局过滤器,会对所有路由⽣效
*/
@Slf4j
@Component // 让容器扫描到,等同于注册了
public class BlackListFilter implements GlobalFilter, Ordered {
// 模拟⿊名单(实际可以去数据库或者redis中查询)
private static List<String> blackList = new ArrayList<>();
static {
blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址
}
/**
* 过滤器核⼼⽅法
* @param exchange 封装了request和response对象的上下⽂
* @param chain ⽹关过滤器链(包含全局过滤器和单路由过滤器)
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 思路:获取客户端ip,判断是否在⿊名单中,在的话就拒绝访问,不在的话就放⾏
// 从上下⽂中取出request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 从request对象中获取客户端ip
String clientIp = request.getRemoteAddress().getHostString();
// 拿着clientIp去⿊名单中查询,存在的话就决绝访问
if(blackList.contains(clientIp)) {
// 决绝访问,返回
response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
log.debug("IP:" + clientIp + " 在⿊名单中,将被拒绝访问! ");
String data = "Request be denied!";
DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
return response.writeWith(Mono.just(wrap));
}
// 合法请求,放⾏,执⾏后续的过滤器
return chain.filter(exchange);
}
/**
* 返回值表示当前过滤器的顺序(优先级),数值越⼩,优先级越⾼
*/
@Override
public int getOrder() {
return 0;
}
}
解决跨域问题:
可以在网关利用springboot提供的CorsWebFilter,对请求头添加跨域的信息
@Configuration
public class CorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//配置跨域
CorsConfiguration config = new CorsConfiguration();
//任意请求头
config.addAllowedHeader("*");
//任意方式
config.addAllowedMethod("*");
//任意请求来源
config.addAllowedOrigin("*");
//允许携带cookie跨域
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
也可以在网关直接配置解决跨域问题:
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowedOrigins: "https://docs.spring.io"
allowedMethods:
- GET
允许https://docs.spring.io
路径下所有GET请求跨域
接口限流:
配置限流的过滤器信息:
server:
port: 8084
spring:
redis:
host: 127.0.0.1
port: 6379
cloud:
gateway:
routes:
- id: user
uri: lb://SERVICE-USER
predicates:
- Path=/house/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@ipKeyResolver}"
GateWay限流是基于Redis使用令牌桶算法实现的,所以要引入Redis依赖,以及配置Redis参数信息
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
参数详解:
- filter 名称必须是 RequestRateLimiter。
- redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求。
- redis-rate-limiter.burstCapacity:令牌桶的容量,允许在 1s 内完成的最大请求数。
- key-resolver:使用 SpEL 按名称引用 bean。
@Configuration
public class AppCoonfig {
@Bean
public KeyResolver ipKeyResolver(){
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getPath().value());
}
};
}
}
GateWay高可用
网关作为非常核心的⼀个部件,如果挂掉,那么所有请求都可能无法路由处理,因此我们需要做GateWay的高可用。
GateWay的高可用很简单: 可以启动多个GateWay实例来实现高可用,在GateWay的上游使用Nginx等负载均衡设备进行负载转发以达到高可用的目的。
启动多个GateWay实例(假如说两个,一个端口9002,⼀个端口9003),剩下的就是使用Nginx等完成负载代理即可。
示例如下:
#配置多个GateWay实例
upstream gateway {
server 127.0.0.1:9002;
server 127.0.0.1:9003;
}
location / {
proxy_pass http://gateway;
}