GateWay简介
Spring Cloud GateWay是Spring Cloud一个全新项目,目的是取代Netflix Zuul,基于 Spring5.0 + SpringBoot2.0 +WebFlux (基于高性能的Reactor模式响应式通信框架Netty,异步非阻塞模型)等技术开发,性能高于Zuul,官方测试GateWay是Zuul的1.6倍,旨在为微服务架构提供一种简单有效的统一的API路由管理方式。
Spring Cloud GateWay提供了统一分路由方式(反向代理)。
同时,基于Filter(定义过滤器对请求过滤)链的方式提供了网关基本功能,例如:鉴权、流量控制、熔断、路径重写、日志监控等。
网关在架构中的位置

图例说明:
(1)Nginx负载均衡器:主要是完成对下游网关组件的负载,网关建立集群主要是为了实现高可用性。
(2)API路由:网关会转发请求到下游的微服务。
GateWay核心概念
Zuul 1.x - 阻塞式IO Zuul 2.x - 基于Netty 非阻塞IO
Spring Cloud GateWay天生是异步非阻塞的,基于Reactor模型。
一个请求,网关会根据一定的条件(断言)进行路由匹配;
匹配成功后就可以将请求转发到指定的服务;
在这个请求过程中,可以进行一些控制(限流、日志、黑白名单等);
- 路由:网关的基础部分,网关的基础工作单元。路由由一个ID、一个目标URL(最终路由到的地址)、一系列的断言(匹配条件判断)和Filter过滤器(精细化控制组成)。如果断言为true,则匹配该路由。
- 断言:参考java8中的断言
java.util.function.Predicate,可以通过匹配http请求中所有内容(请求头、请求参数等), 来确定请求的最终路由。 — 匹配条件 - 过滤器:标准的Spring webFilter, 使用过滤器,可以在请求之前或者之后执行代码逻辑。— 拦截器
GateWay工作过程

(1)客户端向Spring Cloud GateWay发起请求;
(2) 请求会在 GateWay Handler Mapping中找到与请求相匹配的路由,并将其发送给GateWay Web Handler;
(3)Handler 通过指定的过滤器链(pre)来将请求发送到我们实际服务执行业务逻辑;
(4)业务处理完毕返回,会通过请求后的过滤器链(post)将响应内容进行处理;
(5) 返回最终的响应结果给客户端。
pre类型的Filter: 可以做参数校验,权限校验,流量监控,日志输出,协议转换等。 post类型的Filter:可以做响应内容修改,响应头修改,日志输出 ,流量监控等。
GateWay应用
使用网关对下游的微服务进行代理(相当于隐藏下游微服务,对外暴露网关)。
创建网关工程,导入依赖
GateWay不需要使用web模块,引入的是 WebFlux
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><artifactId>lagou-cloud-gateway-9002</artifactId><!--spring boot ⽗启动器依赖--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-commons</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eurekaclient</artifactId></dependency><!--GateWay ⽹关--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-startergateway</artifactId></dependency><!--引⼊webflux--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!--⽇志依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><!--测试依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- Actuator可以帮助你监控和管理Spring Boot应⽤--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starteractuator</artifactId></dependency><!--热部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency></dependencies><dependencyManagement><!--spring cloud依赖版本管理--><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-clouddependencies</artifactId><version>Greenwich.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><!--编译插件--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>11</source><target>11</target><encoding>utf-8</encoding></configuration></plugin><!--打包插件--><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-mavenplugin</artifactId></plugin></plugins></build></project>
application.yml配置
server:port: 9002eureka:client:serviceUrl: # eureka server的路径#把 eureka 集群中的所有url都填写了进来,也可以只写⼀台,因为各个eureka server可以同步注册表defaultZone: http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeurekaserverb:8762/eureka/instance:#使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)prefer-ip-address: true#⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddressinstance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@spring:application:name: lagou-cloud-gatewaycloud:gateway:routes: # 路由可以有多个# 我们⾃定义的路由 ID,保持唯⼀- id: service-autodeliver-router# ⽬标服务地址(部署多实例)动态路由:uri配置的应该是⼀个服务名称,⽽不应该是⼀个具体的服务实例的地址# gateway⽹关从服务注册中⼼获取实例信息然后负载后路由uri: http://127.0.0.1:8096# 断⾔:路由条件,Predicate 接受⼀个输⼊参数,返回⼀个布尔值结果。# 该接⼝包含多种默认⽅法来将 Predicate 组合成其他复杂的逻辑(⽐如:与,或,⾮)。predicates:- Path=/autodeliver/**- id: service-resume-routeruri: http://127.0.0.1:8081 # ⽬标服务地址predicates:- Path=/resume/**filters:# 跳过uri的第一个路径# http://localhost:9002/resume/openstate/1545132 -> http://127.0.0.1:8081/openstate/1545132- StripPrefix=1
GateWay 路由规则
时间点后匹配
spring:cloud:gateway:routes:- id: after_routeuri: https://example.orgpredicates:- After=2017-01-20T17:42:47.789-07:00[America/Denver]
时间点前匹配
spring:cloud:gateway:routes:- id: after_routeuri: https://example.orgpredicates:- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
时间区间匹配
spring:cloud:gateway:routes:- id: after_routeuri: https://example.orgpredicates:- Between=2017-01-20T17:42:47.789-07:00[America/Denver]
指定Cookie正则匹配指定值
spring:cloud:gateway:routes:- id: cookie_routeuri: https://example.orgpredicates:- Cookie=chocolate, ch.p
指定Header正则匹配指定值
spring:cloud:gateway:routes:- id: header_routeuri: https://example.orgpredicates:- Header=X-Request-Id, \d+
请求Host匹配指定值
spring:cloud:gateway:routes:- id: host_routeuri: https://example.orgpredicates:- Host=**.somehost.org,**.anotherhost.org
请求Method匹配指定请求方式
spring:cloud:gateway:routes:- id: method_routeuri: https://example.orgpredicates:- Method=GET,POST
请求路径正则匹配
spring:cloud:gateway:routes:- id: path_routeuri: https://example.orgpredicates:- Path=/red/{segment},/blue/{segment}
请求包含某参数
spring:cloud:gateway:routes:- id: query_routeuri: https://example.orgpredicates:- Query=green
请求包含某参数并且参数值匹配正则表达式
spring:cloud:gateway:routes:- id: query_routeuri: https://example.orgpredicates:- Query=red, gree.
远程地址匹配
spring:cloud:gateway:routes:- id: remoteaddr_routeuri: https://example.orgpredicates:- RemoteAddr=192.168.1.1/24
GateWay动态路由详解
GateWay支持自动从注册中心中获取服务列表并访问,即所谓的动态路由
实现步骤
(1)pom.xml中添加注册中心客户端依赖(eureka-client: 获取注册中心服务列表)
(2) 动态路由配置application.yml
spring:cloud:gateway:routes:- id: service-autodeliver-routeruri: lb://service-autodeliver # 以lb://开头,代表从注册中心获取服务
GateWay过滤器
过滤器简介
按照过滤器生命周期(影响时机点)角度来说,分为两类过滤器 | 生命周期时机点 | 作用 | | —- | —- | | pre | 这种过滤器在请求被路由之前调用。
可利用这种过滤器实现身份验证、在集群中选择 请求的微服务,记录调试信息等。 | | post | 这种过滤器在路由到微服务以后执行。
可用来为相应添加标准的HTTP Header,收集统计信息和指标,将响应从微服务发送给客户端等。 |从过滤器类型角度,分为两类过滤器 | 过滤器类型 | 影响范围 | | —- | —- | | GateWayFilter | 应用到单个路由上 | | GlobalFilter | 应用到所有路由上 |
predicates:- Path=/resume/**filters:# 跳过uri的第一个路径# http://localhost:9002/resume/openstate -> http://127.0.0.1:8081/openstate- StripPrefix=1 # 可以去掉resume之后转发
自定义全局过滤器实现IP访问限制
当请求过来时,判断发送请求的客户端IP,如果在黑名单中,拒绝访问。
自定义GateWay全局过滤器时,实现Global Filter接口,通过全局过滤器可以实现黑白名单、限流等功能。
/*** 定义全局过滤器,会对所有路由⽣效*/@Slf4j@Component // 让容器扫描到,等同于注册了public class BlackListFilter implements GlobalFilter, Ordered {// 模拟⿊名单(实际可以去数据库或者redis中查询)private static List<String> blackList = new ArrayList<>();static {blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址}/*** 过滤器核⼼⽅法* @param exchange 封装了request和response对象的上下⽂* @param chain ⽹关过滤器链(包含全局过滤器和单路由过滤器)* @return*/@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 思路:获取客户端ip,判断是否在⿊名单中,在的话就拒绝访问,不在的话就放⾏// 从上下⽂中取出request和response对象ServerHttpRequest request = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();// 从request对象中获取客户端ipString clientIp = request.getRemoteAddress().getHostString();// 拿着clientIp去⿊名单中查询,存在的话就决绝访问if (blackList.contains(clientIp)) {// 决绝访问,返回response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码log.debug("=====>IP:" + clientIp + " 在⿊名单中,将被拒绝访问!");String data = "Request be denied!";DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());return response.writeWith(Mono.just(wrap));}// 合法请求,放⾏,执⾏后续的过滤器return chain.filter(exchange);}/*** 返回值表示当前过滤器的顺序(优先级),数值越⼩,优先级越⾼* @return*/@Overridepublic int getOrder() {return 0;}}
GateWay高可用
网关作为非常核心的一个部件,如果挂掉,那么所有请求都可能无法路由处理,因此需要做GateWay高可用。
GateWay高可用实现:可以启动多个GateWay实例来实现高可用,在GateWay的上游使用Nginx等负载均衡设备进行负载转发以达到高可用的目的。
#配置多个GateWay实例upstream gateway {server 127.0.0.1:9002;server 127.0.0.1:9003;}location / {proxy_pass http://gateway;}
