基于WebFlux 框架实现的,而WebFlux框架底层则使用了高性能的Reactor 模式通讯框架Netty
对用户请求做身份认证、权限校验,路由到微服务,并实现负载均衡,限流
三大核心概念:Route
路由,Predicate
断言,Filter
过滤器
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
网关搭建步骤
创建项目,引入nacos服务发现和gateway依赖
配置application.yml,包括服务基本信息、nacos地址、路由
路由配置包括:
路由id:路由的唯一标示
路由目标(uri):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡(查询注册中心的服务)
路由断言(predicates):路由断言,判断请求是否符合要求,符合则转发到路由目的地
路由过滤器(filters):路由过滤器,处理请求或响应
配置文件application.yml
server:
port: 10010
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
spring:
application:
name: gateway
cloud:
nacos:
server-addr: nacos:8848 # nacos地址
gateway:
routes:
- id: user-service # 路由标示,必须唯一
#uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址
uri: lb://userservice # 路由的目标地址
predicates: # 路由断言,判断请求是否符合规则
- Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合
filters: # 过滤器
- AddRequestHeader=Truth, Itcast is freaking awesome! # 添加请求头
- id: order-service
uri: lb://orderservice
predicates:
- Path=/order/**
default-filters:
- AddRequestHeader=Truth,Itcast is freaking awesome!
匹配规则
名称 | 说明 | 示例 |
---|---|---|
After | 是某个时间点后的请求 | - After=2037-01-20T17:42:47.789-07:00[America/Denver] |
Before | 是某个时间点之前的请求 | - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai] |
Between | 是某两个时间点之前的请求 | - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver] |
Cookie | 请求必须包含某些cookie | - Cookie=chocolate, ch.p |
Header | 请求必须包含某些header | - Header=X-Request-Id, \d+ |
Host | 请求必须是访问某个host(域名) | - Host=.somehost.org,.anotherhost.org |
Method | 请求方式必须是指定方式 | - Method=GET,POST |
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
Query | 请求参数必须包含指定参数 | - Query=name, Jack或者- Query=name |
RemoteAddr | 请求者的ip必须是指定范围 | - RemoteAddr=192.168.1.1/24 |
Weight | 权重处理 |
Spring提供了31种不同的路由过滤器工厂,对进入网关的请求和微服务返回的响应做处理
过滤器的作用是什么?
对路由的请求或响应做加工处理,比如添加请求头
配置在路由下的过滤器只对当前路由的请求生效
defaultFilters的作用是什么?
对所有路由都生效的过滤器
名称 | 说明 |
---|---|
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除有一个响应头 |
RequestRateLimiter | 限制请求的流量 |
… |
配置文件
gateway:
routes:
- id: user-service # 路由标示,必须唯一
....
filters: # 局部过滤器
- AddRequestHeader=Truth, test1 is freaking awesome! # 添加请求头
default-filters: # 全局过滤器
- AddResponseHeader=Truth,test2 is freaking awesome! # 添加响应头
GatewayFilter
将应用到单个路由或者一个分组的路由上
/**
* 此过滤器功能为计算请求完成时间
*/
@Component
public class ServerGatewayFilter implements GatewayFilter, Ordered {
private static final String ELAPSED_TIME_BEGIN = "Elapsed_Time_Begin";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("ServerGatewayFilter filter ");
// 设置初始时间
exchange.getAttributes().put(ELAPSED_TIME_BEGIN, System.currentTimeMillis());
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
// 获取初始时间
Long startTime = exchange.getAttribute(ELAPSED_TIME_BEGIN);
if (startTime != null) {
// 计算时间差
System.out.println(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms");
}
})
);
}
/*
*过滤器存在优先级,order越大,优先级越低
*/
@Override
public int getOrder() {
return 0;
}
}
定义好ServerGatewayFilter
以后,其需要跟Route
绑定使用,不能在application.yml
文件中配置使用
@Slf4j
@Configuration
public class GatewayRoutesConfiguration {
/**
* java 配置 server 服务路由
* @param builder
* @return
*/
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
log.info("ServerGatewayFilter filtet........");
return builder.routes()
.route(r ->
r.path("/server/**")
// 去掉前缀1节
.filters(f -> f.stripPrefix(1))
// 注册自定义过滤器
.filters(new ServerGatewayFilter())
// 给定id
.id("user-service")
// 转发路由
.uri("lb://cloud-discovery-server")
).build();
}
}
对所有路由都生效的过滤器,并且可以自定义处理逻辑
实现GlobalFilter接口
添加@Order注解或实现Ordered接口
编写处理逻辑
// @Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
/**
* 处理当前请求,有必要的话通过{@link GatewayFilterChain}将请求交给下一个过滤器处理
*
* @param exchange 请求上下文,里面可以获取Request、Response等信息
* @param chain 用来把请求委托给下一个过滤器
* @return {@code Mono} 返回标示当前过滤器业务结束
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1.获取请求参数
ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, String> params = request.getQueryParams();
// 2.获取参数中的 authorization 参数
String auth = params.getFirst("authorization");
// 3.判断参数值是否等于 admin
if ("admin".equals(auth)) {
// 4.是,放行
return chain.filter(exchange);
}
// 5.否,拦截
// 5.1.设置状态码
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
// 5.2.拦截请求
return exchange.getResponse().setComplete();
}
@Override
public int getOrder() {
return -1;
}
}
路由过滤器、defaultFilter、全局过滤器的执行顺序?
order值越小,优先级越高
当order值一样时,顺序是defaultFilter最先,然后是局部的路由过滤器,最后是全局过滤器
每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。
可以参考下面几个类的源码来查看:
org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()
方法是先加载defaultFilters
,然后再加载某个route的filters,然后合并。
org.springframework.cloud.gateway.handler.FilteringWebHandler#handle()
方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链
Gateway 网关 之 限流 - 参考资料
本篇文字Gateway中限流介绍的是,RequestRateLimiter
是基于Redis
和Lua
脚本实现的令牌桶算法。
配置文件属性参考:RequestRateLimiterGatewayFilterFactory及内部类 Config,RateLimiter,KeyResolver 的字段
spring:
cloud:
gateway:
routes:
- id: member-demo
uri: lb://my-member
predcates:
- name: Path
args:
- patterns=/member/**,/my-member/**
filters:
# 配置令牌桶
- name: RequestRateLimiter
args:
# 配置类:RateLimiter 实现类 RedisRateLimiter 对象
redis-rate-limiter:
defaultRateLimiter:
defaultConfig:
# 每秒生成令牌个数
replenishRate: 1
# 令牌桶上线容量
burstCapacity: 5
# 自定义一个 keyResolver 实现 KeyResolver MyKeyResolver,注入容器,使用Spring的EL表达式读取值
defaultKeyResolver: '#{@myKeyResolver}'
# 令牌桶使用到 Redis ,需要配置Redis连接属性
redis:
host: 127.0.0.1
port: 6379
password: xxx
自定义令牌桶规则:
just()
方法参数表示根据什么进行限流。实例中是根据主机名进行限流。参数值影响Redis
@Component
public class MyKeyResolver implements KeyResolver {
/**
* 字符串泛型,代表令牌分给谁,可以是IP地址也可以是数字字符串,如果返回 "1",表示所有客户端一表只有一个令牌
*
* @param exchange
* @return
*/
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
String remoteAddr = request.getRemoteAddress().getAddress().getHostAddress();
// 对每个客户端IP进行限流
return Mono.just(remoteAddr);
}
}