Spring Cloud Gateway是在Spring生态系统之上构建的API网关服务,它旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。html
Spring Cloud Gateway 具备以下特性:spring
Spring Cloud Gateway 做为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在Spring Cloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然仍是使用的 Zuul 2.0以前的非Reactor模式的老版本。(有一个版本的说法是 Zuul2.x 的连续跳票和 Zuul1.x 的性能并非很理想,从而催生了Spring Cloud Gateway。)而为了提高网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通讯框架Netty。json
网上不少地方都说 Zuul 是阻塞的,Spring Cloud Gateway 是非阻塞的,这么说并不严谨的,准确的讲 Zuul1.x 是阻塞的,而在2.x的版本中,Zuul也是基于Netty,也是非阻塞的,若是必定要说性能,其实这个真没多大差距。Zuul的有关信息能够参考《网关Zuul科普》。cookie
Spring Cloud Gateway有如下几个重要的概念,后面的内容也主要围绕这几个概念展开。架构
定义2个服务:hello-server和user-server,他们分别都注册到euraka服务上,示例以下:app
在未通过网关时,咱们能够经过如下2个接口来分别访问hello-server和user-server:框架
http://localhost:8081/hello
http://localhost:8082/user
如今咱们来定义一个Spring Cloud Gateway服务,相关的Maven依赖以下:curl
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
application.yml文件中添加以下配置:ide
server:
port: 9091
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: path_route_1
uri: http://localhost:8082/user
predicates:
- Path=/user
各字段含义以下:函数
这段配置的意思是:配置了一个id为 path_route_1的URI代理规则,当访问地址 http://localhost:9091/user
时,会路由到地址http://localhost:8082/user
。
Spring Cloud Gateway的路由配置和Zuul的路由配置有较大的区别,玩过Zuul的注意一下二者的区分。
启动类以下,平平无奇:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
如今咱们启动Spring Cloud Gateway服务,而后请求http://localhost:9091/user:
hidden:~ hidden$ curl -i localhost:9091/user
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 11
Date: Thu, 02 Apr 2020 09:28:58 GMT
User Info!
因为没有配置过hello-server的服务,因此还不能经过网关访问它:
hidden:~ hidden$ curl -i localhost:9091/hello
HTTP/1.1 404 Not Found
Content-Type: application/json
Content-Length: 133
{"timestamp":"2020-04-02T09:30:09.840+0000","path":"/hello","status":404,"error":"Not Found","message":null,"requestId":"8b0a00e1-4"}
除了使用yml配置文件的方式,还能够经过代码(Java Bean)来实现路由配置。咱们在启动类中添加方法 customRouteLocator() 来定制转发规则,详情以下:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
return builder.routes()
.route("path_route_2", r -> r.path("/hello")
.uri("http://localhost:8081/hello"))
.build();
}
}
重启服务,再次访问hello-server的接口,能够看到访问成功:
hidden:~ hidden$ curl -i localhost:9091/hello
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 19
Date: Thu, 02 Apr 2020 09:53:25 GMT
Hello!
Spring Cloud Gateway将路由匹配做为Spring WebFlux HandlerMapping基础架构的一部分。Spring Cloud Gateway 包括许多内置的 Predicate 工厂,这些 Predicate 工厂经过不一样的 HTTP 请求参数来匹配,多个 Predicate 工厂能够组合使用。
Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其余复杂的逻辑(好比:与,或,非)。能够用于接口请求参数校验、判断新老数据是否有变化须要进行更新操做。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各类路由匹配规则,有经过 Header、请求参数等不一样的条件来进行做为条件匹配到对应的路由。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。
说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来咱们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。
带有指定Cookie的请求会匹配改路由。将application.yml中的spring.cloud.gateway.routes配置修改成以下内容:
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8082/user
predicates:
- Cookie=username,pipisi
此时使用curl工具发送带有cookie为 username=pipisi 的请求就能够匹配改路由:
curl -i localhost:9091/user --cookie "username=pipisi"
带指定查询参数的请求能够匹配该路由。将application.yml中的spring.cloud.gateway.routes配置修改成以下内容:
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8082/user
predicates:
- Query=username
此时使用curl工具发送带有username=pipisi 查询参数的请求就能够匹配改路由:
curl -i localhost:9091/user?username=pipisi
其他的还有(参考上图):经过Header匹配(将predicates中的 -Query 换成 -Header,如下类同)、经过Host匹配(-Host)、经过请求方法匹配(-Method)、经过IP地址匹配(-RemoteAddr)、经过请求时间(-After、-Before、-Between)、经过权重(-Weight)以及最开始在入门示例中就说起的经过请求路径匹配(-Path)。还能够进行组合使用,好比:
predicates:
- Query=username
- Cookie=username,pipisi
- Path=/user
各类 Predicates 同时存在于同一个路由时,请求必须同时知足全部的条件才被这个路由匹配。
网关常常须要对路由请求进行过滤,执行一些操做,如鉴权以后构造头部之类的。过滤的种类不少,如增长请求头、增长请求参数 、增长响应头以及断路器等等,这就用到了Spring Cloud Gateway 的 过滤器(Filter)。
当咱们有不少服务时,好比前面所说起的user-server和hello-server,客户端请求各个服务的API接口时,每一个服务都要作相同的事情,好比鉴权、限流、日志等,以下图(上半部)。
对于这样重复的工做,能够在微服务的上一层加一个全局的权限控制、限流、日志的API网关服务,而后将请求转发到具体的业务服务层。这个API网关服务就是起到一个服务边界的做用,外界的请求访问系统,必须先经过网关层,如上图(下半部)。
Spring Cloud Gateway同 Zuul 相似,也有 pre
和 post
两种方式的过滤器:pre 过滤器在请求被路由以前调用,咱们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等;post 过滤器在路由到微服务之后执行,这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
Spring Cloud Gateway的 Filter生命周期以下图所示。
与 Zuul 不一样的是,Filter除了分为pre
和 post
两种方式的 Filter 外,在Spring Cloud Gateway中,Filter 根据做用范围可分为另外两种,一种是针对于单个路由的 GatewayFilter,它在配置文件中的写法同 Predicate相似;另一种是针对于全部路由的 GlobalFilter。
过滤器容许以某种方式修改传入的HTTP Request 或传出的HTTP Response,它能够限定做用在某些特定请求路径上。Spring Cloud Gateway包含了30多个内置的GatewayFilter工厂,好比AddRequestHeaderGatewayFilterFactory(添加请求头的过滤器工厂)、AddRequestParameterGatewayFilterFactory(添加请求参数的过滤器工厂)、AddResponseHeaderGatewayFilterFactory(添加响应头的过滤器工厂)、HystrixGatewayFilterFactory(Hystrix熔断过滤器工厂)、RequestRateLimiterGatewayFilterFactory(请求限流的过滤器工厂)等(所有的GatewayFilter工厂能够查看jar包中org.springframework.cloud.gateway.filter.factory目录,除了这些,你也能够自定义本身的过滤器)。
GatewayFilter工厂与前面介绍的Predicate工厂相似,都是在配置文件 application.yml 中配置,遵循了约定大于配置的思想,只须要在配置文件配置GatewayFilter Factory的名称,而不须要写所有的类名,好比 AddRequestHeaderGatewayFilterFactory 只须要在配置文件中写AddRequestHeader,而不是所有类名。官方文档都给出了这些过滤器工厂详细的使用案例,下面挑选1个案例以做演示。
application.yml配置以下:
server:
port: 9091
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8082/user
filters:
- AddRequestHeader=X-Request-Foo, Bar
predicates:
- Path=/user
过滤器工厂会在匹配的请求头加上一对请求头“x-request-foo=Bar”。能够在下游的user-server服务中使用@RequestHeader来查看所接收请求的请求头信息。
Zuul 做为网关结合注册中心进行使用时,默认状况下 Zuul 会根据注册中心注册的服务列表,以服务名为路径建立动态路由,Spring Cloud Gateway一样也实现了该功能。下面咱们演示下 Spring Cloud Gateway结合注册中心如何使用默认的动态路由。
首先添加Maven依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
其次修改application.yml配置文件:
server:
port: 9091
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
重启服务以后,咱们就能够像 Zuul 那样采用下面的方式访问接口了:
http://localhost:9091/hello-server/hello
http://localhost:9091/user-server/user
ReferencesSpring Cloud Gateway 的核心处理流程如上图,核心逻辑就是路由转发,执行过滤器链。Gateway的客户端回向Spring Cloud Gateway发起请求,而后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再经过指定的过滤器链来将请求发送到咱们实际的服务执行业务逻辑,而后返回。在Filter链中,经过虚线分割Filter的缘由是:过滤器能够在转发请求以前处理或者接收到被代理服务的返回结果以后处理。全部的Pre类型的Filter执行完毕以后,才会转发请求到被代理的服务处理。被代理的服务把全部请求完毕以后,才会执行Post类型的过滤器。