GateWay新一代网关
https://spring.io/projects/spring-cloud-gateway
这篇文章写的非常好
SpringCloud Gateway简介
- Spring Cloud Gateway是Spring Cloud的一个全新项目,基于Spring 5.0+Spring Boot 2.0和Project Reactor等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的API路由管理方式。
- Spring Cloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
- Spring Cloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,如:安全、监控/指标、和限流。
- Spring Cloud Gateway底层使用了高性能的通信框架Netty。
Spring cloud Gateway特性
- 基于Spring Framework 5,Project Reactor和Spring Boot 2.0。
- 集成Hystrix断路器。
- 集成Spring Cloud DiscoveryClient(SpringCloud服务发现)。
- Predicates和Filters作用于特定路由,易于编写的Predicates和Filters。
- 具备一些网关的高级功能:动态路由、限流、路径重写
三大核心概念:
Route(路由)
:路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由,目标URI会被访问。Predicate(断言)
:这是一个java 8的Predicate,可以使用它来匹配来自HTTP请求的任何内容,如:请求头和请求参数。断言的输入类型是一个ServerWebExchange。Filter(过滤器)
:指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者后对请求进行修改。总结:
- web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。predicate就是匹配条件,而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标URI,就可以实现具体的路由了。
Spring Cloud Gateway和框架
- Spring的Webflux的响应式编程不仅仅是编程风格的改变,而且对于一系列的著名框架,都提供了响应式访问的开发包,比如Netty、Redis等。
- SpringCloud Gateway使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。
- Gateway是基于异步非阻塞模型上进行开发的,性能方面不需要担心。
Webflux模型
Webflux模型替换了旧的Servlet线程模型。用少量的线程处理request和response io操作,这些线程称为Loop线程,而业务交给响应式编程框架处理,响应式编程是非常灵活的,用户可以将业务中阻塞的操作提交到响应式框架的work线程中执行,而不阻塞的操作依然可以在Loop线程中进行处理,大大提高了Loop线程的利用率
Spring Cloud Gateway的处理流程
客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。Handler再通过指定的过滤器链来将请求发送到实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(Pre)或之后(post)执行业务逻辑
- 配置路由
Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- actuator监控信息完善-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
routes:
- id: payment-routh #路由的id,要求唯一
uri: http://localhost:8001 #匹配提供服务的路由地址
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment-routh2 #路由的id,要求唯一
uri: http://localhost:8001 #匹配提供服务的路由地址
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
register-with-eureka: true
fetch-registry: true
http://localhost:8001/payment/get/** -----> http://localhost:9527/payment/get/**
RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
.maxTrustedIndex(1);
...
.route("direct-route",
r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
.uri("https://downstream1")
.route("proxied-route",
r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24")
.uri("https://downstream2")
)
业务需求:通过9527网关访问外网的百度新闻
动态路由:
uri的协议是lb
Spring Cloud Gateway匹配规则
SpringCloud Gateway是通过Spring WegFlux的HandlerMapping作为底层支持来匹配到转发路由,Spring Cloud Gateway内置了很多Predicates工厂,这些Predicates工厂通过不同的HTTP请求参数来匹配,多个Predicates工厂可以组合使用。
Predicate断言条件介绍
Predicate来源于Java 8,是Java 8中引入的一个函数,Predicate接收一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。
在Spring Cloud Gateway中Spring利用Predicate的特性实现了各种路由匹配规则,有通过Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。
SpringCloud内置的集中Predicate的实现,如下图:
Predicate官网
After,匹配在指定日期时间之后发生的请求
Cookie:
Header:
获取时区:
public class Test {
public static void main(String[] args) {
ZonedDateTime now = ZonedDateTime.now(); // 默认时区
System.out.println(now);
}
}
Spring Cloud gateway高级功能
实现熔断降级
在分布式系统中,网关作为流量的入口,因此会有大量的请求进入网关,向其他服务发起调用,其他服务不可避免的会出现调用失败(超时,异常),失败时不能让请求堆积在网关上,需要快速失败并返回给客户端,想要实现这个要求,就必须在网关上做熔断、降级操作
自定义过滤器,implements GlobalFilter, Ordered
/**
* @author swl
*/
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("******全局过滤器*******");
String name = exchange.getRequest().getQueryParams().getFirst("name");
if(name == null){
log.info("******非法用户:null*****");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 加载顺序
* @return
*/
@Override
public int getOrder() {
return 0;
}
}