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(定义过滤器对请求过滤)链的方式提供了网关基本功能,例如:鉴权、流量控制、熔断、路径重写、日志监控等。

网关在架构中的位置

image.png
图例说明:
(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, 使用过滤器,可以在请求之前或者之后执行代码逻辑。— 拦截器

image.png

GateWay工作过程

image.png
(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

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
  5. http://maven.apache.org/xsd/maven-4.0.0.xsd">
  6. <modelVersion>4.0.0</modelVersion>
  7. <artifactId>lagou-cloud-gateway-9002</artifactId>
  8. <!--spring boot ⽗启动器依赖-->
  9. <parent>
  10. <groupId>org.springframework.boot</groupId>
  11. <artifactId>spring-boot-starter-parent</artifactId>
  12. <version>2.1.6.RELEASE</version>
  13. </parent>
  14. <dependencies>
  15. <dependency>
  16. <groupId>org.springframework.cloud</groupId>
  17. <artifactId>spring-cloud-commons</artifactId>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.springframework.cloud</groupId>
  21. <artifactId>spring-cloud-starter-netflix-eurekaclient</artifactId>
  22. </dependency>
  23. <!--GateWay ⽹关-->
  24. <dependency>
  25. <groupId>org.springframework.cloud</groupId>
  26. <artifactId>spring-cloud-startergateway</artifactId>
  27. </dependency>
  28. <!--引⼊webflux-->
  29. <dependency>
  30. <groupId>org.springframework.boot</groupId>
  31. <artifactId>spring-boot-starter-webflux</artifactId>
  32. </dependency>
  33. <!--⽇志依赖-->
  34. <dependency>
  35. <groupId>org.springframework.boot</groupId>
  36. <artifactId>spring-boot-starter-logging</artifactId>
  37. </dependency>
  38. <!--测试依赖-->
  39. <dependency>
  40. <groupId>org.springframework.boot</groupId>
  41. <artifactId>spring-boot-starter-test</artifactId>
  42. <scope>test</scope>
  43. </dependency>
  44. <!-- Actuator可以帮助你监控和管理Spring Boot应⽤-->
  45. <dependency>
  46. <groupId>org.springframework.boot</groupId>
  47. <artifactId>spring-boot-starteractuator</artifactId>
  48. </dependency>
  49. <!--热部署-->
  50. <dependency>
  51. <groupId>org.springframework.boot</groupId>
  52. <artifactId>spring-boot-devtools</artifactId>
  53. <optional>true</optional>
  54. </dependency>
  55. </dependencies>
  56. <dependencyManagement>
  57. <!--spring cloud依赖版本管理-->
  58. <dependencies>
  59. <dependency>
  60. <groupId>org.springframework.cloud</groupId>
  61. <artifactId>spring-clouddependencies</artifactId>
  62. <version>Greenwich.RELEASE</version>
  63. <type>pom</type>
  64. <scope>import</scope>
  65. </dependency>
  66. </dependencies>
  67. </dependencyManagement>
  68. <build>
  69. <plugins>
  70. <!--编译插件-->
  71. <plugin>
  72. <groupId>org.apache.maven.plugins</groupId>
  73. <artifactId>maven-compiler-plugin</artifactId>
  74. <configuration>
  75. <source>11</source>
  76. <target>11</target>
  77. <encoding>utf-8</encoding>
  78. </configuration>
  79. </plugin>
  80. <!--打包插件-->
  81. <plugin>
  82. <groupId>org.springframework.boot</groupId>
  83. <artifactId>spring-boot-mavenplugin</artifactId>
  84. </plugin>
  85. </plugins>
  86. </build>
  87. </project>

application.yml配置

  1. server:
  2. port: 9002
  3. eureka:
  4. client:
  5. serviceUrl: # eureka server的路径
  6. #把 eureka 集群中的所有url都填写了进来,也可以只写⼀台,因为各个eureka server可以同步注册表
  7. defaultZone: http://lagoucloudeurekaservera:8761/eureka/,http://lagoucloudeurekaserverb:8762/eureka/
  8. instance:
  9. #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip)
  10. prefer-ip-address: true
  11. #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
  12. instance-id: ${spring.cloud.client.ipaddress}:${spring.application.name}:${server.port}:@project.version@
  13. spring:
  14. application:
  15. name: lagou-cloud-gateway
  16. cloud:
  17. gateway:
  18. routes: # 路由可以有多个
  19. # 我们⾃定义的路由 ID,保持唯⼀
  20. - id: service-autodeliver-router
  21. # ⽬标服务地址(部署多实例)动态路由:uri配置的应该是⼀个服务名称,⽽不应该是⼀个具体的服务实例的地址
  22. # gateway⽹关从服务注册中⼼获取实例信息然后负载后路由
  23. uri: http://127.0.0.1:8096
  24. # 断⾔:路由条件,Predicate 接受⼀个输⼊参数,返回⼀个布尔值结果。
  25. # 该接⼝包含多种默认⽅法来将 Predicate 组合成其他复杂的逻辑(⽐如:与,或,⾮)。
  26. predicates:
  27. - Path=/autodeliver/**
  28. - id: service-resume-router
  29. uri: http://127.0.0.1:8081 # ⽬标服务地址
  30. predicates:
  31. - Path=/resume/**
  32. filters:
  33. # 跳过uri的第一个路径
  34. # http://localhost:9002/resume/openstate/1545132 -> http://127.0.0.1:8081/openstate/1545132
  35. - StripPrefix=1

GateWay 路由规则

image.png

时间点后匹配

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: after_route
  6. uri: https://example.org
  7. predicates:
  8. - After=2017-01-20T17:42:47.789-07:00[America/Denver]

时间点前匹配

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: after_route
  6. uri: https://example.org
  7. predicates:
  8. - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

时间区间匹配

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: after_route
  6. uri: https://example.org
  7. predicates:
  8. - Between=2017-01-20T17:42:47.789-07:00[America/Denver]

指定Cookie正则匹配指定值

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: cookie_route
  6. uri: https://example.org
  7. predicates:
  8. - Cookie=chocolate, ch.p

指定Header正则匹配指定值

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: header_route
  6. uri: https://example.org
  7. predicates:
  8. - Header=X-Request-Id, \d+

请求Host匹配指定值

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: host_route
  6. uri: https://example.org
  7. predicates:
  8. - Host=**.somehost.org,**.anotherhost.org

请求Method匹配指定请求方式

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: method_route
  6. uri: https://example.org
  7. predicates:
  8. - Method=GET,POST

请求路径正则匹配

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: path_route
  6. uri: https://example.org
  7. predicates:
  8. - Path=/red/{segment},/blue/{segment}

请求包含某参数

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: query_route
  6. uri: https://example.org
  7. predicates:
  8. - Query=green

请求包含某参数并且参数值匹配正则表达式

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: query_route
  6. uri: https://example.org
  7. predicates:
  8. - Query=red, gree.

远程地址匹配

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: remoteaddr_route
  6. uri: https://example.org
  7. predicates:
  8. - RemoteAddr=192.168.1.1/24

GateWay动态路由详解

GateWay支持自动从注册中心中获取服务列表并访问,即所谓的动态路由

实现步骤

(1)pom.xml中添加注册中心客户端依赖(eureka-client: 获取注册中心服务列表)
(2) 动态路由配置application.yml

  1. spring:
  2. cloud:
  3. gateway:
  4. routes:
  5. - id: service-autodeliver-router
  6. uri: lb://service-autodeliver # 以lb://开头,代表从注册中心获取服务

GateWay过滤器

过滤器简介

  • 按照过滤器生命周期(影响时机点)角度来说,分为两类过滤器 | 生命周期时机点 | 作用 | | —- | —- | | pre | 这种过滤器在请求被路由之前调用。
    可利用这种过滤器实现身份验证、在集群中选择 请求的微服务,记录调试信息等。 | | post | 这种过滤器在路由到微服务以后执行。
    可用来为相应添加标准的HTTP Header,收集统计信息和指标,将响应从微服务发送给客户端等。 |

  • 从过滤器类型角度,分为两类过滤器 | 过滤器类型 | 影响范围 | | —- | —- | | GateWayFilter | 应用到单个路由上 | | GlobalFilter | 应用到所有路由上 |

  1. predicates:
  2. - Path=/resume/**
  3. filters:
  4. # 跳过uri的第一个路径
  5. # http://localhost:9002/resume/openstate -> http://127.0.0.1:8081/openstate
  6. - StripPrefix=1 # 可以去掉resume之后转发

自定义全局过滤器实现IP访问限制

当请求过来时,判断发送请求的客户端IP,如果在黑名单中,拒绝访问。
自定义GateWay全局过滤器时,实现Global Filter接口,通过全局过滤器可以实现黑白名单、限流等功能。

  1. /**
  2. * 定义全局过滤器,会对所有路由⽣效
  3. */
  4. @Slf4j
  5. @Component // 让容器扫描到,等同于注册了
  6. public class BlackListFilter implements GlobalFilter, Ordered {
  7. // 模拟⿊名单(实际可以去数据库或者redis中查询)
  8. private static List<String> blackList = new ArrayList<>();
  9. static {
  10. blackList.add("0:0:0:0:0:0:0:1"); // 模拟本机地址
  11. }
  12. /**
  13. * 过滤器核⼼⽅法
  14. * @param exchange 封装了request和response对象的上下⽂
  15. * @param chain ⽹关过滤器链(包含全局过滤器和单路由过滤器)
  16. * @return
  17. */
  18. @Override
  19. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  20. // 思路:获取客户端ip,判断是否在⿊名单中,在的话就拒绝访问,不在的话就放⾏
  21. // 从上下⽂中取出request和response对象
  22. ServerHttpRequest request = exchange.getRequest();
  23. ServerHttpResponse response = exchange.getResponse();
  24. // 从request对象中获取客户端ip
  25. String clientIp = request.getRemoteAddress().getHostString();
  26. // 拿着clientIp去⿊名单中查询,存在的话就决绝访问
  27. if (blackList.contains(clientIp)) {
  28. // 决绝访问,返回
  29. response.setStatusCode(HttpStatus.UNAUTHORIZED); // 状态码
  30. log.debug("=====>IP:" + clientIp + " 在⿊名单中,将被拒绝访问!");
  31. String data = "Request be denied!";
  32. DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
  33. return response.writeWith(Mono.just(wrap));
  34. }
  35. // 合法请求,放⾏,执⾏后续的过滤器
  36. return chain.filter(exchange);
  37. }
  38. /**
  39. * 返回值表示当前过滤器的顺序(优先级),数值越⼩,优先级越⾼
  40. * @return
  41. */
  42. @Override
  43. public int getOrder() {
  44. return 0;
  45. }
  46. }

GateWay高可用

网关作为非常核心的一个部件,如果挂掉,那么所有请求都可能无法路由处理,因此需要做GateWay高可用。
GateWay高可用实现:可以启动多个GateWay实例来实现高可用,在GateWay的上游使用Nginx等负载均衡设备进行负载转发以达到高可用的目的。

  1. #配置多个GateWay实例
  2. upstream gateway {
  3. server 127.0.0.1:9002;
  4. server 127.0.0.1:9003;
  5. }
  6. location / {
  7. proxy_pass http://gateway;
  8. }