一、概述
SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 2.0之前的非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
Spring Cloud Gateway 的目标,不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。
二、Gateway核心概念
一.核心概念
1、Route(路由)
Route 是网关的基础元素,由 ID、目标 URI、断言、过滤器组成。当请求到达网关时,由 Gateway Handler Mapping 通过断言进行路由匹配(Mapping),当断言为真时,匹配到路由。
2、Filter(过滤器)
Filter 是 Gateway 中的过滤器,可以在请求发出前后进行一些业务上的处理。
3、Predicate(断言)
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
二.工作流程
三、整合案例(Spring Cloud Gateway整合Eureka动态路由转发)
一.引入依赖(添加web依赖会报错,网关不需要web,因为gateway用的是webflux)
<!-- SpringCloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
二.配置文件
Spring Gateway支持两种方式提供路由服务,其一是配置文件启用,其二则是通过代码达到目的,建议使用第一种。
1、eureka配置
eureka:
instance:
hostname: cloud-gateway
instance-id: cloud-gateway9527
prefer-ip-address: true #访问路径可以显示IP地址
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 5
# Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 20
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka8001:8001/eureka/,http://eureka8002:8002/eureka/,http://eureka8003:8003/eureka/ #集群版
2、gateway配置(配置文件实现,看起来比较清晰)
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由转发。
#路由是多个
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://CLOUD-PAYMENT-SERVICE #匹配后提供服务的路由地址,忽略大小写,lb的意思是负载均衡
predicates:#断言
- Path=/payment/get/** # 路径相匹配的进行路由,意思就是问服务端有没有这个路径,有的话进行转发。
3、测试访问
测试访问该地址,可以转发到CLOUD-PAYMENT-SERVICE对应的服务方法。
http://localhost:9527/payment/get/2
四、断言的使用(YML配置案例)
一.Path
最常用断言,可以在一个router里配置多个path
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
uri: lb://CLOUD-PAYMENT-SERVICE #匹配后提供服务的路由地址,忽略大小写,lb的意思是负载均衡
predicates:#断言
- Path=/payment/get1/**,/payment/get2/**
二.After
设定一个UTC时间,此时间之后的请求会成功,此时间之前的请求会404
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
三.Before
设定一个UTC时间,此时间之前的请求会成功,此时间之后的请求会404
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
四.Between
Between 路由断言 Factory有两个参数,datetime1和datetime2。在datetime1和datetime2之间的请求将被匹配。datetime2参数的实际时间必须在datetime1之后。
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://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
Cookie 路由断言 Factory有两个参数,cookie名称和正则表达式。请求包含次cookie名称且正则表达式为真的将会被匹配。
cookie为键值对,后面的可以为具体的值,也可以为正则表达式的匹配。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Cookie=username, mobanbai#此处为键值对
测试
curl http://localhost:9527/payment/lb --cookie "username=mobanbai"
六.Header
Header 路由断言 Factory有两个参数,header名称和正则表达式。请求包含次header名称且正则表达式为真的将会被匹配。
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://example.org
predicates:
- Header=X-Request-Id, \d+
测试
curl http://localhost:9527/payment/lb --H "X-Request-Id=12"
七.Method
Method 路由断言 Factory只包含一个参数: 需要匹配的HTTP请求方式
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://example.org
predicates:
# 只有POST请求才会被转发
- Method=POST
八.Query
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=baz
九.RemoteAddr
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://example.org
predicates:
- RemoteAddr=192.168.1.1/24
五、过滤器
一.过滤器的生命周期
1、pre
前置过滤器
2、post
后置过滤
二.过滤器的种类
1、单一过滤器
2、全局过滤器
三.自定义过滤器
1、实现接口
全局过滤器要实现两个接口GlobalFilter, Ordered
@Component
public class AuthFilter implements GlobalFilter, Ordered
{
// 排除过滤的 uri 地址
// swagger排除自行添加
private static final String[] whiteList = {"/auth/login", "/user/register", "/system/v2/api-docs",
"/auth/captcha/check", "/auth/captcha/get","/auth/login/slide"};
@Resource(name = "stringRedisTemplate")
private ValueOperations<String, String> ops;
/**
* 过滤器执行方法
* 处理 Web 请求并(可选)委托给下一个
* {@code WebFilter} 通过给定的 {@link GatewayFilterChain}。
* @param exchange 当前服务器交换
* @param 链提供了一种委托给下一个过滤器的方法
* @return {@code Mono<Void>} 指示请求处理何时完成
**/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
String url = exchange.getRequest().getURI().getPath();
log.info("url:{}", url);
// 跳过不需要验证的路径
if (Arrays.asList(whiteList).contains(url))
{
return chain.filter(exchange);
}
String token = exchange.getRequest().getHeaders().getFirst(Constants.TOKEN);
// token为空
if (StringUtils.isBlank(token))
{
return setUnauthorizedResponse(exchange, "token can't null or empty string");
}
String userStr = ops.get(Constants.ACCESS_TOKEN + token);
if (StringUtils.isBlank(userStr))
{
return setUnauthorizedResponse(exchange, "token verify error");
}
JSONObject jo = JSONObject.parseObject(userStr);
String userId = jo.getString("userId");
// 查询token信息
if (StringUtils.isBlank(userId))
{
return setUnauthorizedResponse(exchange, "token verify error");
}
// 设置userId到request里,后续根据userId,获取用户信息
ServerHttpRequest mutableReq = exchange.getRequest().mutate().header(Constants.CURRENT_ID, userId)
.header(Constants.CURRENT_USERNAME, jo.getString("loginName")).build();
ServerWebExchange mutableExchange = exchange.mutate().request(mutableReq).build();
return chain.filter(mutableExchange);
}
private Mono<Void> setUnauthorizedResponse(ServerWebExchange exchange, String msg)
{
ServerHttpResponse originalResponse = exchange.getResponse();
originalResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
originalResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
byte[] response = null;
try
{
response = JSON.toJSONString(R.error(401, msg)).getBytes(Constants.UTF8);
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
DataBuffer buffer = originalResponse.bufferFactory().wrap(response);
return originalResponse.writeWith(Flux.just(buffer));
}
@Override
public int getOrder()
{
return -200;
}
}
2、ServerWebExchange(服务网络交换器)
ServerWebExchange的注释: ServerWebExchange是一个HTTP请求-响应交互的契约。提供对HTTP请求和响应的访问,并公开额外的服务器端处理相关属性和特性,如请求属性。
其实,ServerWebExchange命名为服务网络交换器,存放着重要的请求-响应属性、请求实例和响应实例等等,有点像Context的角色。
public interface HttpMessage {
// 获取请求头,目前的实现中返回的是ReadOnlyHttpHeaders实例,只读
HttpHeaders getHeaders();
}
public interface ReactiveHttpInputMessage extends HttpMessage {
// 返回请求体的Flux封装
Flux<DataBuffer> getBody();
}
public interface HttpRequest extends HttpMessage {
// 返回HTTP请求方法,解析为HttpMethod实例
@Nullable
default HttpMethod getMethod() {
return HttpMethod.resolve(getMethodValue());
}
// 返回HTTP请求方法,字符串
String getMethodValue();
// 请求的URI
URI getURI();
}
public interface ServerHttpRequest extends HttpRequest, ReactiveHttpInputMessage {
// 连接的唯一标识或者用于日志处理标识
String getId();
// 获取请求路径,封装为RequestPath对象
RequestPath getPath();
// 返回查询参数,是只读的MultiValueMap实例
MultiValueMap<String, String> getQueryParams();
// 返回Cookie集合,是只读的MultiValueMap实例
MultiValueMap<String, HttpCookie> getCookies();
// 远程服务器地址信息
@Nullable
default InetSocketAddress getRemoteAddress() {
return null;
}
// SSL会话实现的相关信息
@Nullable
default SslInfo getSslInfo() {
return null;
}
// 修改请求的方法,返回一个建造器实例Builder,Builder是内部类
default ServerHttpRequest.Builder mutate() {
return new DefaultServerHttpRequestBuilder(this);
}
interface Builder {
// 覆盖请求方法
Builder method(HttpMethod httpMethod);
// 覆盖请求的URI、请求路径或者上下文,这三者相互有制约关系,具体可以参考API注释
Builder uri(URI uri);
Builder path(String path);
Builder contextPath(String contextPath);
// 覆盖请求头
Builder header(String key, String value);
Builder headers(Consumer<HttpHeaders> headersConsumer);
// 覆盖SslInfo
Builder sslInfo(SslInfo sslInfo);
// 构建一个新的ServerHttpRequest实例
ServerHttpRequest build();
}
}
3、GatewayFilterChain(网关过滤链表)
/**
* 网关过滤链表接口
* 用于过滤器的链式调用
*/
public interface GatewayFilterChain {
/**
* 链表启动调用入口方法*/
Mono<Void> filter(ServerWebExchange exchange);
}