前言
API 网关的定义
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
Gateway提供了一个用于在Spring MVC之上构建API网关的库。Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注,例如:安全性,监视/度量和弹性。
Spring Cloud Gateway是spring Cloud d的一个全新的项目 ,Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,Spring Cloud Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。
Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。
Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。
比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。
一、Gateway的特性
gateway之所以性能好,因为底层使用WebFlux,而webFlux底层使用netty通信(NIO),
动态路由:能够匹配任何请求属性;
集成Hystrix的短路器功能;
集成Spring Cloud 服务发现功能;
可以对路由指定Predicate(断言)和Filter(过滤器);
二、GateWay与zuul的区别
在SpringCloud F版之前,推荐的网关是Netflix的Zuu:
- Zuul 1.x 是一个基于阻塞的 I/O Api Gateway;
- Zuul 1.x 基于Servlet 2.5使用阻塞架构它不支持任何长链接(如WebSocket),Zuul的设计模式和Nginx比较像,每次I/O都是从工作线程中选择一个去执行,请求线程被阻塞到工作线程完成,但是差别是Nginx基于C++实现,Zuul是使用java实现的,而JVM本身会有第一次加载比较慢的情况,使得Zuul的性能比较差。
- Zuul 2.x 的理念比较先进,想基于Netty非阻塞和长连接,但是spring Cloud暂时还没有整合,Zuul 2.x相较于Zuul 1.x 性能有较大提升。 但是在性能方面,根据官方测试,Spring Cloud Gateway的Rps(美妙请求数)是Zuul的1.6倍。
- Spring Cloud Gateway建立在spring Framework 5、Project Reactor和Spring boot 2.0以上。使用非阻塞Api。同时还支持WebSocket
三、GateWay的一些重要概念
四、案例
说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。
通过请求路径匹配
Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。
spring:
application:
name: cloud-gateway
cloud:
gateway:
#开启从注册中心动态创建路由功能,利用微服务进行路由
discovery:
locator:
enabled: true
routes:
- id: payment_route1
uri: lb://cloud-payment-service
predicates:
- Path=/get/**
各字段含义如下:
- id:我们自定义的路由 ID,保持唯一
- uri:目标服务地址 ,开启从注册中心动态创建路由功能,利用微服务进行路由之后可以使用
lb://cloud-payment-service
进行负载均衡 - predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
这里我们准备四个项目:
7001:位eureka的注册中心
8001/8002:提供接口的微服务 cloud-payment-service
9527:Gateway网关
7001,8001,8002不多介绍了https://blog.csdn.net/flycp/article/details/108432834可以异步之前的博客看一下:
我们着重说一下,cloud-gateway9527这个网关项目:
pom.xml: 引入Eureka以及Gateway相关依赖
<!--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>
application.yml: 这里我们直接演示集群版本通过微服务进行路由,并进行负载均衡
单机版:discovery.locator.enabled: false
设置 uri: http://localhost:8001即可
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
#开启从注册中心动态创建路由功能,利用微服务进行路由
discovery:
locator:
enabled: true
routes:
- id: payment_route1
uri: lb://cloud-payment-service
predicates:
- Path=/get/**
eureka:
instance:
hostname: cloud-gateway-service #eureka服务注册地址
client:
#是否从eureka Server 抓取已有的注册信息,默认true,单节点无所谓,集群必须为true才能配合ribbon使用负载均衡
fetch-registry: true
#表示是否将自己加入eureka service true代表加入
register-with-eureka: true
service-url:
# #设置 eureka server 交互的地址查询服务和注册服务需要依赖的地址
defaultZone: http://eureka7001.com:7001/eureka
主启动: 只需添加EnableEurekaClient
注解即可
@SpringBootApplication
@EnableEurekaClient
public class Gateway9527 {
public static void main(String[] args) {
SpringApplication.run(Gateway9527.class,args);
}
}
我们启动项目:访问 http://localhost:9527/get/1 可以直接转发到http://localhost:8001/get/1的接口
从而可以做到,隐藏真实的服务端口,而提供相应的服务,增加了服务的安全性。
这是诸多配置里面的一种,按照请求路劲进行(predicates)断言到相应的服务中去,除此之外,还有很多
此处不多演示了:
# 通过路径进行断言路由
- Path=/get/**
# 通过请求时间 如果请求时间不在对应时间之后则请求失败
# - After=2020-09-19T06:06:06+08:00[Asia/Shanghai]
# 通过请求时间 如果请求时间不在对应时间之前则请求失败
# - Before=2020-09-19T06:06:06+08:00[Asia/Shanghai]
# 通过cookie
# - Cookie=cpown, cpown
下面有一张图可以简单地看一下所有的可配置元素:
五、实现java编码配置Route
上面讲了通过配置文件,配置相应的 Gateway predicates,还有另一种方式进行配置那就是代码配置:
先注释掉application.yml里面的配置:
可以在启动类里面添加Bean的注册,也可以创建自己的Config文件:
@SpringBootApplication
@EnableEurekaClient
public class Gateway9527 {
public static void main(String[] args) {
SpringApplication.run(Gateway9527.class,args);
}
//添加Bean实例
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/get/**")
.uri("lb://cloud-payment-service"))
.build();
}
}
效果也是一样的。
五、实现java编码配置Filter
Spring Cloud Gateway 除了上述的路由 Route 与 Preicate断言以外,还支持请求的过滤以及增强操作:Filter
有点类似于我们之前的说的 :过滤器interceptor
package com.cpown.springcloud.config;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class MygateWayFilter implements GlobalFilter, Ordered {
/**
* 类似于 过滤器interceptor 可以对请求进行判断,增强,以及校验
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
String name = request.getQueryParams().getFirst("name");
//如果参数为 空 不予通过 直接报错
if (StringUtils.isEmpty(name)) {
response.setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return response.setComplete();
}
return chain.filter(exchange);
}
/**
* 此方法仅设置排序顺序无需改动
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
我们 创建一个MygateWayFilter
全局过滤组件组件,可以对所有的请求进行Filter处理,判断是否包含参数 “name”,如果不包含则不予通过,包含则通过:
再次请求:http://localhost:9527/get/1 发现已经无法通过。
修改请求为:http://localhost:9527/get/1?name=cc 又可以恢复调用。