一、什么是服务网关
服务网关是Spring Cloud 官方推出的第二代网关框架,用于替代第一代网关Netflix Zuul,其不仅提供了路由方式,并且基于Filter 链的方式提供了网关的基本功能;服务网关作为整个分布式服务的流量入口,有着举足轻重的作用,比如:
- 协议转换,路由转发
- 流量聚合,对流量进行监控,日志输出
- 在网关层进行流量的控制
- 在网关层做权限校验
二、服务网关的实现原理
Gateway 的核心组件是路由和过滤器,不同于Netflix Zuul 的是,Gateway 多了一个断言,用来判断请求交给哪一个Gateway web handler 进行处理。流程如下
首先请求交给Gateway handler mapping处理,如果请求和断言匹配,则将请求交给Gateway web handler处理。Gateway web handler处理请求时会经过一系列Filter链。如图所示,过滤器链被虚线一分为二,左边半部分是过滤器发送代理请求之前处理的,即pre 阶段,执行左边半部分一般可以实现限流、日志输出、鉴权等。右边半部分是请求之后处理的,即post 阶段,执行右边半部分可以实现对响应数据的修改,比如更改响应头和转换协议等;
三、断言工厂
当一个请求到来时,首先交给断言工厂进行处理,如果命中断言工厂,则执行下一步,否则返回错误信息;服务网关Gateway 内置了多个断言工厂,具体如下:
1、AfterRoutePredicateFactory断言工厂
spring:
cloud:
gateway:
routes:
-id: after_routes # 路由id,要保证id唯一。
uri: lb://alibaba-nacos # 请求转发路径
predicate:
-After= 2010-01-20T17:42:47.789-07:00[America/Denver] # 表示服务只能在配置日期后访问,并转发至http://alibaba-nacos/xxx
2、BeforeRoutePredicateFactory
spring:
cloud:
gateway:
routes:
-id: before_routes # 路由id,要保证id唯一。
uri: lb://alibaba-nacos # 请求转发路径
predicate:
-Before= 2010-01-20T17:42:47.789-07:00[America/Denver] # 表示服务只能在配置日期前访问,并转发至http://alibaba-nacos/xxx
3、BetweenRoutePredicateFactory
spring:
cloud:
gateway:
routes:
-id: between_routes # 路由id,要保证id唯一。
uri: lb://alibaba-nacos # 请求转发路径
predicate:
-Between= 2017-01-20T17:42:47.789-07:00[America/Denver], 2027-01-21T17:42:47.789-07:00[America/Denver] # 表示服务只能在配置日期时间段访问,并转发至http://alibaba-nacos/xxx
注意: 关于时间断言工厂,设置时有个讨巧的点就是通过ZonedDateTime.now() 打印时间包含时区
4、CookieRoutePredicateFactory
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: lb://alibaba-nacos
predicates:
# 当且仅当带有名为somecookie,并且值符合正则ch.p的Cookie时,才会转发到用户微服务
# 如Cookie满足条件,则访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Cookie=somecookie, ch.p
5、HeaderRoutePredicateFactory
spring:
cloud:
gateway:
routes:
- id: header_route
uri: lb://alibaba-nacos
predicates:
# 当且仅当带有名为X-Request-Id,并且值符合正则\d+的Header时,才会转发到用户微服务
# 如Header满足条件,则访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Header=X-Request-Id, \d+
6、HostRoutePredicateFactory
spring:
cloud:
gateway:
routes:
- id: host_route
uri: lb://alibaba-nacos
predicates:
# 当且仅当名为Host的Header符合**.somehost.org或**.anotherhost.org时,才会转发用户微服务
# 如Host满足条件,则访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Host=**.somehost.org,**.anotherhost.org
7、MethodRoutePredicateFactory
spring:
cloud:
gateway:
routes:
- id: method_route
uri: lb://alibaba-nacos
predicates:
# 当且仅当HTTP请求方法是GET时,才会转发用户微服务
# 如请求方法满足条件,访问http://localhost:8040/** -> user-center/**
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Method=GET
8、PathRoutePreidicateFactory
spring:
cloud:
gateway:
routes:
- id: path_route
uri: lb://alibaba-nacos
predicates:
# 当且仅当访问路径是/users/*或者/some-path/**,才会转发用户微服务
# segment是一个特殊的占位符,单层路径匹配
# eg. 访问http://localhost:8040/users/1 -> user-center/users/1
- Path=/users/{segment},/some-path/**
9、QueryRoutePredicateFactory
spring:
cloud:
gateway:
routes:
- id: query_route
uri: lb://user-center
predicates:
# 当且仅当请求带有名为foo的参数,且参数值符合正则ba.,才会转发到用户微服务
# eg. 访问http://localhost:8040/users/1?baz=baz -> user-center的/users/1?baz=baz
- Query=foo, ba.
10、RemoteaddRoutePredicateFactory
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: lb://user-center
predicates:
# 当且仅当请求IP是192.168.1.1/24网段,例如192.168.1.10,才会转发到用户微服务
# eg. 访问http://localhost:8040/users/1 -> user-center的/users/1
- RemoteAddr=192.168.1.1/24
四、自定义断言工厂
如果说上边提供的系统断言工厂,不满足业务需求,那么我们可以实现自定义的断言工厂,通过观看源码得知,上述默认的断言工厂均继承于AbstractRoutePredicateFactory.那么我们要实现断言工厂首先需要继承AbstractRoutePredicateFactory ,并且需要注意的是: 自定义断言工厂类名后缀必须是xxxRoutePredicateFactory;
下面实现一个我们服务只能在每天指定时间段访问,具体实现如下:
package com.corn.gateway;
import com.google.common.collect.Lists;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import java.util.function.Predicate;
/**
* 自定义gateway 断言
* @Author corn
* @Date 2021/3/23 14:37
*/
@Slf4j
@Component
public class TimeBetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeBetweenRoutePredicateFactory.TimeBetweenConfig> {
public TimeBetweenRoutePredicateFactory() {
super(TimeBetweenConfig.class);
}
/**
*@描述 gateway 断言具体执行逻辑
*@参数
*@返回值
*@创建人 corn
*@创建时间 2021/3/23
*/
@Override
public Predicate<ServerWebExchange> apply(TimeBetweenConfig config) {
LocalTime start = config.getStart();
LocalTime end = config.getEnd();
return exchange->{
return LocalTime.now().isAfter(start)&&LocalTime.now().isBefore(end);
};
}
/**
*@描述 用来将配置文件中的配置和TimeBetweenConfig 中的属性做映射
*@参数
*@返回值
*@创建人 corn
*@创建时间 2021/3/23
*/
@Override
public List<String> shortcutFieldOrder() {
return Lists.newArrayList("start","end");
}
/**
*@描述 设置参数
*@参数
*@返回值
*@创建人 corn
*@创建时间 2021/3/29
*/
@Data
protected static class TimeBetweenConfig {
private LocalTime start;
private LocalTime end;
}
public static void main(String[] args) {
// 获取gateway 支持的时间格式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
String format = dateTimeFormatter.format(LocalTime.now());
System.out.println(format);
}
}
配置如下:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: lb://alibaba-nacos
predicates:
- TimeBetween=上午5:00,下午11:59 # 这样就实现了我们的服务只能每天的早上5点到晚上的11:59 运行访问,其他时间段是不允许访问的
总结
本节讲述了服务网关gateway 的背景,组成部分,原理和断言工厂,断言其实决定了请求被路由Router 中,然后在断言处理之后,会进入过滤链Filter 逻辑中处理