Spring Cloud Gateway(一)
Gateway简介
- Spring Cloud Gateway用于在Spring WebFlux或Spring WebMVC之上构建API网关。Spring Cloud Gateway旨在提供一种简单而有效的方式来提供统一的路由访问方式,并为它们提供横切关注点,例如:安全、监控、指标和限流等,基于Netty的响应式开发模式。
- Gateway的特点
- 基于Spring和Spring Boot构建;
- 能够匹配任何请求属性的路由;
- 断言和过滤器是特定于路由的,重写断言和过滤器很方便;
- 集成了断路器和Spring Cloud DiscoveryClient;
- 限制请求速率;
- 重写路径。
- 核心概念
-
路由(route):路由是网关最基础部分,路由信息由一个ID、一个目的URL、一组断言工厂和一组过滤器组成。如果断言为真,则说明请求URL和配置的路由匹配。
-
断言(predicate):Gateway中的断言函数输入类型是ServerWebExchange,允许开发者自定义匹配来自Http请求中的任何信息,比如请求头和参数。
-
过滤器(Filter):一个标准的web过滤器,Gateway中的过滤器分为GatewayFilter和GlobalFilter,过滤器可以对请求和响应进行处理。
路由配置
- 创建示例工程api_gateway_server
- 创建工程导入坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jzy</groupId>
<artifactId>api_gateway_server</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
</project>
- 配置启动类
package org.jzy.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServerApplication.class, args);
}
}
- 编写配置文件
# application.yml
server:
port: 8090
spring:
application:
name: api-gateway-server
cloud:
gateway:
routes: # 配置路由:路由id,路由到微服务的uri,断言
- id: product-service
uri: http://localhost:9001
predicates: # 路由条件
- Path=/product/**
filters: # 路由过滤器
- RewritePath=/product-service/(?<segment>.*),/$\(segment)
discovery:
locator:
enabled: true # 开启根据微服务名称自动转发
lower-case-service-id: true # 微服务名称小写
- RoutePredicateFactory接口实现类实现了多种类型的断言。
# 路由断言之后的匹配
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://localhost:9001
predicates:
- After=xxx
# 路由断言之前的匹配
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://localhost:9001
predicates:
- Before=xxx
# 路由断言Header匹配,比如名称匹配X-Request-Id并且表达式匹配\d+
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://localhost:9001
predicates:
- Header=X-Request-id, \d+
# 路由断言Host匹配
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:9001
predicates:
- Host=**.org,**.com
# 路由断言可变参数匹配
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:9001
predicates:
- Path=/foo/{segment},/bar/{segment}
# 路由断言查询参数匹配
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://localhost:9001
predicates:
- Query=baz
- 动态路由,也就是面向微服务的路由服务,设置uri从注册中心获取。
过滤器
-
Gateway的Filter只有pre和post。pre这种过滤器在请求被路由之前调用,可以用来实现身份验证、记录日志信息等。post这种过滤器在路由到微服务以后执行,这种过滤器可以用来响应添加标准的HTTP Header、收集统计信息和指标、把响应从微服务发送给客户端等。
-
GatewayFilter应用到单个路由或一个分组的路由,而GlobalFilter应用到所有的路由。
-
局部过滤器GatewayFilter是针对单个路由的过滤器,可以对访问的URL过滤,进行切面处理。Gateway中内置了很多不同类型的局部过滤器,比如RewritePathGatewayFilterFactory类可以重写原始的请求路径。
-
全局过滤器GlobalFilter作用于所有的路由,Gateway定义了GlobalFilter接口,用户可以自定义。通过全局过滤器可以实现统一鉴权,安全性验证等功能,并且全局过滤器也是使用最多的过滤器。
-
自定义全局过滤器示例
package org.jzy.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class MyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//执行过滤器中的业务逻辑
System.out.println("执行自定义全局过滤器");
//继续往后执行
return chain.filter(exchange);
}
@Override
public int getOrder() {
//指定过滤器的执行顺序,返回值越小,执行优先级越高
return 0;
}
}
统一鉴权
- 鉴权逻辑是当客户端第一次请求服务时,服务端对用户进行信息认证。当认证通过后把用户信息进行加密形成token,返回客户端。以后每次请求,客户端都携带认证的token,服务端对token进行解密,判断token是否过期。
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class AuthenticationFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//请求参数没有携带access-token则认证失败,结束请求
String token = exchange.getRequest().getQueryParams().getFirst("access-token");
if (token == null) {
System.out.println("登录失败");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
网关限流
一、常见的限流算法
-
计数器限流算法(固定窗口限流算法)是最简单的限流实现算法,本质是通过维护一个单位时间的计数器,每次请求计数器加1,当单位时间计数器累加到大于设定的阈值,则以后的请求都被拒绝,直到单位时间已经过去,再将计数器重置为零。
-
滑动窗口限流算法是对计数器限流算法的改进,把时间窗口分为多个小周期,每个小周期有自己的计数器。随着时间的滑动,过小的小周期数据被删除,从而更精确地控制流量。比如每分钟处理60个请求,可以把1分钟分为60个窗口,每个窗口一秒只能处理不大于60个请求的数量。滑动窗口算法能够更加平滑地处理流量,减少计数器算法的突增问题。
-
漏桶限流算法的原理是控制数据注入网络的速度,平滑网络上的突发流量。它像一个漏桶一样以一定的速度处理进入的请求,超过漏桶容量的请求被拒绝或等待。这种算法适用于需要控制整体流量速率的情况。
-
令牌桶限制算法是比较常见的限流算法,基于令牌桶的概念,所有的请求在处理前都需要拿到一个可用的令牌才会被处理。令牌以一定的速率被添加到桶中,当桶满时,新添加的令牌被丢弃或拒绝。请求到达后首先获取令牌桶中的令牌,处理完业务逻辑后,令牌被删除。令牌桶有最低限额,当桶中的令牌达到最低限额时,请求处理完之后将不会删除令牌,以此保证足够的限流。
二、基于Filter的限流
-
Gateway官方提供了令牌桶的限流支持,基于内置的过滤器工厂实现。在过滤器工厂中是通过Redis和Lua脚本结合的方式进行流量控制。
-
使用Filter限流
- 引入Redis依赖
<!-- redis依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<!-- 监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 添加配置
spring:
redis:
host: localhost
port: 6379
database: 0
cloud:
gateway:
routes: # 配置路由:路由id,路由到微服务的uri,断言
- id: product-service
uri: http://localhost:9001
predicates:
- Path=/product/**
filters:
- name: RequestRateLimiter
args:
# 使用SpEL表达式从上下文中获取
key-resolver: '#{@pathKeyResolver}'
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 3
- 配置redis中key的解析器
package org.jzy.gateway.configuration;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver pathKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getPath().toString());
}
};
}
}
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver pathKeyResolver() {
//基于请求路径限流
return exchange -> Mono.just(exchange.getRequest().getPath().toString());
}
@Bean
public KeyResolver userKeyResolver() {
//基于请求参数的限流
return exchange -> Mono.just(exchange.getRequest()
.getQueryParams().getFirst("userId"));
}
}
三、基于Sentinel限流
- Sentinel支持对Gateway、Zuul等主流的API网关进行限流。