SpringCloud早期整合了Netflix 公司的开源项目Zuul 1.0作为网关,但后期Zuul 推出了2.0,但 Spring Cloud 貌似没有整合计划,看了下spring-cloud-netflix-zuul引用的还是1.0的Zuul,Spring Cloud 后面推出了自己的服务网关:Spring Cloud Gateway,使用起来比 Zuul 更简单,配置更方便。
而作为一个网关,应当具备以下功能:
性能:API高可用,负载均衡,容错机制。
安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
日志:日志记录(spainid,traceid)一旦涉及分布式,全链路跟踪必不可少。
缓存:数据缓存。
监控:记录请求响应数据,api耗时分析,性能监控。
限流:流量控制,错峰流控,可以定义多种限流规则。
灰度:线上灰度部署,可以减小风险。
路由:动态路由规则。
要知道网关,先说明一下网关中常见的三大术语:
1)Filter(过滤器):
和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。
2)Route(路由):
网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
3)Predicate(断言):
这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。
这里我们以这段配置为例子:
server:
port: 8081
spring:
cloud:
gateway:
routes:
- id: lb_route
uri: https://www.baidu.com
predicates:
- Path=/mypath1
- id: lb_route2
uri: https://www.taobao.com
predicates:
- Path=/mypath2
- After=2021-08-18T18:00:00+08:00[Asia/Shanghai]
这段配置的含是:配置了二个route(lb_route,lb_route2),当访问localhost:8081/mypath1会转发到https://www.baidu.com:443/mypath1,当访问localhost:8081/mypath2,且在配置的时间(此时间为UTC时间格式)之后才会转发到https://www.taobao.com:443/mypath2。
Predicate接口在JDK8中的定义:
Predicate<T> 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。add--与、or--或、negate--非
- boolean test(T t); 判断
- Predicate<T> and(Predicate<? super T> other) 接收一个Predicate类型,也就是将传入的条件和当前条件以并且(AND)的关系组合
- Predicate<T> or(Predicate<? super T> other)接收一个Predicate类型,也就是将传入的条件和当前条件以或(OR)的关系组合
说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,其实Predicate的默认实现有很多,网上有张图总结了spring cloud-gateway内置的几种实现
规则 | 实例 | 说明 |
---|---|---|
Path | - Path=/gate/,/rule/ | ## 当请求的路径为gate、rule开头的时,转发到http://localhost:9023服务器上 |
Before | - Before=2017-01-20T17:42:47.789-07:00[America/Denver] | 在某个时间之前的请求才会被转发到 http://localhost:9023服务器上 |
After | - After=2017-01-20T17:42:47.789-07:00[America/Denver] | 在某个时间之后的请求才会被转发 |
Between | - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] | 在某个时间段之间的才会被转发 |
Cookie | - Cookie=chocolate, ch.p | 名为chocolate的表单或者满足正则ch.p的表单才会被匹配到进行请求转发 |
Header | - Header=X-Request-Id, \d+ | 携带参数X-Request-Id或者满足\d+的请求头才会匹配 |
Host | - Host=www.hd123.com | 当主机名为www.hd123.com的时候直接转发到http://localhost:9023服务器上 |
Method | - Method=GET | 只有GET方法才会匹配转发请求,还可以限定POST、PUT等请求方式 |
也就是说通过简简单单的几个配置即可实现怎么多功能,那它如何实现的呢?我们来看看RoutePredicateFactory
可以看到这是一个接口,并且被@FunctionalInterface注解修饰,被此注解修饰说明这是一个函数式接口(函数式接口的就是一个接口有且只有一个抽象方法,那么该接口就是一个函数式接口),apply是它唯一的抽象方法,我们再看看apply的具体实现类
这里我们随便点进去一个XXXRoutePredicateFactory
发现都是return一个GatewayPredicate,而GatewayPredicate是一个接口,它继承了Predicate,而Predicate也是一个函数式接口。
也就是通过test()方法,返回是否匹配路由条件,那它是在何时调用的test()方法进行判断的呢,来看一波gateway的请求流程图。
Spring-Cloud-Gateway请求整体流程图
从图中可以看到在RoutePredicateHandlerMapping的lookupRoute来匹配路由是否可用。
首先通过routeLocator的getRoutes方法获取所有路由(读取配置文件转成路由),然后得到对应路由的predicate,然后进入apply方法,就可以看到它真正调用了对应的PathRoutePredicateFactory的test方法()
参考文章:
https://www.cnblogs.com/webor2006/p/8135873.html