⼀、网关介绍

1.1 锋迷商城微服务拆分之后遇到的问题?

当我们对锋迷商城进⾏微服务拆分之后,不同的接⼝是由不同的服务提供的,不同的服务部署在不同的服务器上,因此前端进⾏接⼝调⽤的时候访问不同的接⼝会请求不同的ip和port,如果将接⼝服务的访问地址在前端代码中固定写死:

  • 前端需要记录很多服务器地址列表
  • 当服务被迁移到不同的服务器上的时候,就必须修改前端代码才能继续访问
  • 当对服务进⾏集群部署的时候,没有办法实现负载均衡

1.2 什么是API网关?

使用服务网关作为接口服务的统⼀代理,前端通过网关完成服务的统⼀调用

16、Gateway - 图1

1.3 网关可以干什么?

  • 路由:接口服务的统⼀代理,实现前端对接口服务的统⼀访问
  • 过滤:对用户请求进行拦截、过滤(用户鉴权)、监控
  • 限流:限制用户的访问流量

1.4 常用的网关

  • Nginx
  • Spring Cloud Netflix zuul
  • Spring Cloud Gateway

⼆、使用Nginx实现网关服务

Nginx通常被用作应用服务器网关,服务网关通常使用zuul或者gateway

16、Gateway - 图2

三、使用Gateway实现网关服务

netflix zuul使用文档
链接: http://note.youdao.com/noteshare?id=b53758f34f40cb67357ee8ec13d37afd

16、Gateway - 图3

3.1 搭建gateway服务器

  • 创建SpringBoot应用,添加gateway依赖

3.2 配置路由规则

  • application.yml
  1. server:
  2. port: 9999
  3. spring:
  4. application:
  5. name: gateway-server
  6. cloud:
  7. gateway:
  8. routes:
  9. # 配置api-service1路由规则
  10. - id: api-service1
  11. uri: http://localhost:8001
  12. predicates:
  13. - Path=/product/**
  14. # 配置api-service2路由规则
  15. - id: api-service2
  16. uri: http://localhost:8002
  17. predicates:
  18. - Path=/order/**

四、 Gateway工作原理

4.1 名词解释

Route: 路由是网关的基本组成部分,它是由id、目标uri、断言组成,如果断言为true,则匹配该路由,转向到当
前路由的URI

Predicate:断言,用户请求的匹配规则

Filter:过滤器,用于对请求进行前置、后置处理(可以在网关实现对请求或相应的加工处理)

4.2 Gateway⼯作流程图

16、Gateway - 图4

五、 Predicate断言

SpringCloud Gateway提供了多种断言匹配的方式:

  • After
  • Before
  • Between
  • Cookie
  • Header
  • Host
  • Method
  • Path
  • Query
  • RemoteAddr
  • Weight

5.1 Path

根据请求路径的正则匹配

  1. spring:
  2. application:
  3. name: gateway-server
  4. cloud:
  5. gateway:
  6. routes:
  7. - id: aaa
  8. uri: http://localhost:8001
  9. predicates:
  10. - Path=/product/**
  11. - id: bbb
  12. uri: http://localhost:8002
  13. predicates:
  14. - Path=/order/**

5.2 Query

根据请求携带的参数匹配路由

  1. spring:
  2. application:
  3. name: gateway-server
  4. cloud:
  5. gateway:
  6. routes:
  7. - id: aaa
  8. uri: http://localhost:8001
  9. predicates:
  10. # 如果请求url中带有name参数 ---> http://localhost:8001
  11. - Query=name
  12. - id: bbb
  13. uri: http://localhost:8002
  14. predicates:
  15. #如果请求url中带有pwd参数 ---> http://localhost:8002
  16. - Query=pwd

5.3 Header

根据Header中携带的参数匹配

  1. spring:
  2. application:
  3. name: gateway-server
  4. cloud:
  5. gateway:
  6. routes:
  7. - id: aaa
  8. uri: http://localhost:8001
  9. predicates:
  10. - Header=token
  11. - id: bbb
  12. uri: http://localhost:8002
  13. predicates:
  14. - Header=aa,haha

六、过滤器

6.1 Gateway内置网关过滤器

gateway网关可以对用户的请求和响应进行处理, gateway提供了多个内置的过滤器,不同的过滤器可以完成
不同的请求或者响应的处理

AddRequestHeader 在请求头中添加参数

AddRequestParameter 添加请求参数

AddResponseHeader

Spring Cloud Gateway

6.4. The DedupeResponseHeader GatewayFilter Factory

6.5. The Hystrix GatewayFilter Factory

6.6. Spring Cloud CircuitBreaker GatewayFilter Factory

6.7. The FallbackHeaders GatewayFilter Factory

6.8. The MapRequestHeader GatewayFilter Factory

6.9. The PrefixPath GatewayFilter Factory

6.10. The PreserveHostHeader GatewayFilter Factory

6.11. The RequestRateLimiter GatewayFilter Factory

6.12. The RedirectTo GatewayFilter Factory

6.13. The RemoveRequestHeader GatewayFilter Factory

6.14. RemoveResponseHeader GatewayFilter Factory

6.15. The RemoveRequestParameter GatewayFilter Factory

6.16. The RewritePath GatewayFilter Factory

6.17. RewriteLocationResponseHeader GatewayFilter Factory

6.18. The RewriteResponseHeader GatewayFilter Factory

6.19. The SaveSession GatewayFilter Factory

6.20. The SecureHeaders GatewayFilter Factory

6.20. The SecureHeaders GatewayFilter Factory

6.21. The SetPath GatewayFilter Factory

6.22. The SetRequestHeader GatewayFilter Factory

6.23. The SetResponseHeader GatewayFilter Factory

6.24. The SetStatus GatewayFilter Factory

6.25. The StripPrefix GatewayFilter Factory

6.26. The Retry GatewayFilter Factory

6.27. The RequestSize GatewayFilter Factory

6.28. The SetRequestHostHeader GatewayFilter Factory

6.29. Modify a Request Body GatewayFilter Factory

6.30. Modify a Response Body GatewayFilter Factory

6.31. Default Filters

  1. spring:
  2. application:
  3. name: gateway-server
  4. cloud:
  5. gateway:
  6. routes:
  7. - id: aaa
  8. uri: http://localhost:8001
  9. predicates:
  10. - Path=/red/aaa/product/**
  11. filters:
  12. - AddRequestHeader=token,wahahaawahaha
  13. - AddRequestParameter=username, ergou
  14. - SetStatus=404
  15. # - RewritePath=/red(?<segment>/?.*), $\{segment}
  16. - StripPrefix=2

6.2 自定义服务过滤器

6.2.1 创建网关过滤器 - 实现GatewayFilter

  • 实现GatewayFilter, Ordered
  1. public class MyFilter01 implements GatewayFilter, Ordered {
  2. @Override
  3. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  4. ServerHttpRequest request = exchange.getRequest();
  5. ServerHttpResponse response = exchange.getResponse();
  6. System.out.println("---------------⾃定义过滤器");
  7. return chain.filter(exchange);
  8. }
  9. @Override
  10. public int getOrder() {
  11. return 0;
  12. }
  13. }
  • 配置过滤器
  1. @Configuration
  2. public class GatewayConfig {
  3. @Bean
  4. public RouteLocator routeLocator(RouteLocatorBuilder builder){
  5. System.out.println("-----------------------init");
  6. RouteLocator routeLocator = builder.routes().route( r->
  7. r.path("/product/**") // predicates
  8. .filters(f->f.filters( new MyFilter01() )) // filters
  9. .uri("http://localhost:8001") //uri
  10. ).build();
  11. return routeLocator;
  12. }
  13. }

6.2.2 创建网关过滤器 - 继承AbstractNameValueGatewayFilterFactory

相当于扩展Gateway内置的网关过滤器

  1. /**
  2. * 创建⼀个类继承AbstractNameValueGatewayFilterFactory,类名必须以GatewayFilterFactory结尾,类名前⾯的部
  3. 分即为当前⾃定义⽹关过滤器的 名字
  4. * 添加@Component注解,注册到Spring容器
  5. */
  6. @Component
  7. public class MyFilterGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
  8. @Override
  9. public GatewayFilter apply(NameValueConfig config) {
  10. System.out.println("name:"+config.getName());
  11. System.out.println("value:"+config.getValue());
  12. //创建⾃定义⽹关过滤器并返回
  13. GatewayFilter gatewayFilter = new GatewayFilter() {
  14. @Override
  15. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  16. System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~⾃定义⽹关过滤器");
  17. return chain.filter(exchange);
  18. }
  19. };
  20. return gatewayFilter;
  21. }
  22. }

配置:

  1. spring:
  2. application:
  3. name: gateway-server
  4. cloud:
  5. gateway:
  6. routes:
  7. - id: bbb
  8. uri: http://localhost:8002
  9. predicates:
  10. - Path=/order/**
  11. filters:
  12. - MyFilter=aa,bb

6.3 全局过滤器

上述我们讲到的过滤器,都是配置在某个路由/服务中,我们称之为网关服务过滤器, Gateway提供了内置的
全局过滤器,会拦截过滤所有到达网关服务器的请求。

内置的全局过滤器默认生效,无需开发者干预。

根据业务的需求我们也可以自定义全局过滤器以实现对所有网关请求的拦截和处理。

  1. @Component
  2. public class MyGlobalFilter implements GlobalFilter, Ordered {
  3. @Override
  4. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  5. System.out.println("----------------------------------------------MyGlobalFilter");
  6. List<String> list = exchange.getRequest().getHeaders().get("token");
  7. if (list != null && list.size()>0){
  8. String token = list.get(0);
  9. System.out.println("token:"+token);
  10. return chain.filter(exchange);
  11. }else{
  12. //如果没有token,或者token过期
  13. ServerHttpResponse response = exchange.getResponse();
  14. //设置响应头
  15. response.getHeaders().add("Content-Type","application/json;charset=utf-8");
  16. //设置状态码
  17. response.setStatusCode(HttpStatus.UNAUTHORIZED);
  18. // 封装响应数据
  19. String str = "";
  20. DataBuffer dataBuffer = response.bufferFactory().wrap(str.getBytes());
  21. return response.writeWith(Mono.just(dataBuffer));
  22. }
  23. }
  24. @Override
  25. public int getOrder() {
  26. return 0;
  27. }
  28. }

七、 Gateway动态路由配置

如果在Gateway网关的路由配置中,直接将服务的ip port配置进去,将导致:

1.如果服务的地址变更,必须要重新配置gateway的路由规则

2.如果服务采用集群部署,则不能实现负载均衡

Gateway动态路由
16、Gateway - 图5

7.1 Gateway服务器添加eureka-server依赖

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  4. </dependency>

7.2 配置网关路由

  • application.yml

    1. server:
    2. port: 9999
    3. spring:
    4. application:
    5. name: gateway-server
    6. main:
    7. web-application-type: reactive
    8. cloud:
    9. gateway:
    10. routes:
    11. - id: aaa
    12. uri: lb://api-service1
    13. predicates:
    14. - Path=/product/**
    15. - id: bbb
    16. uri: lb://api-service2
    17. predicates:
    18. - Path=/order/**
    19. eureka:
    20. client:
    21. service-url:
    22. defaultZone: http://localhost:8761/eureka

八、网关限流

8.1 网关限流介绍

服务的统⼀访问

使用过滤器实现鉴权

使用网关实现限流:通过限制用户的请求进⼊到服务中,有效控制应用系统的QPS,达到保护系统的目的

  • 在互联网应用的生产环境中,可以由于用户增长超过预期、竞争对手恶意请求等外界的原因都有可能导致服务器无法处理超预期的高并发请求导致服务器宕机。

8.2 网关限流的常见算法

8.2.1 计数器算法

就是通过记录在单位 时间请求的数量,在这个时间周期内达到设置定值之后就拒绝后续请求的进⼊。

  • 缺点:如果用户请求频率不均匀,有导致短时间、高并发的风险

8.2.2 漏桶算法

就是通过控制“漏桶”流出的速率以限制到达服务的用户流量

  • 缺点:网关会承载比较大的压力

8.2.3 令牌桶算法

就是所有进入网关的请求必须从令牌桶中得到令牌才可以进行服务调用,我们可以通过控制令牌桶的容量、令牌的
产生速率达到控制用户流量的目的。

16、Gateway - 图6

8.3 Gateway网关限流

Gateway是基于令牌桶算法,使用redis作为“桶”结合顾虑器实现了网关限流

8.3.1 添加依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.apache.commons</groupId>
  7. <artifactId>commons-pool2</artifactId>
  8. <version>2.9.0</version>
  9. </dependency>

8.3.2 配置keyResolver

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public KeyResolver keyResolver() {
  5. //http://localhost:9999/order/query?user=1
  6. //使⽤请求中的user参数的值作为令牌桶的key
  7. //return exchange ->Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
  8. return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
  9. }
  10. }

8.3.3 配置服务的限流规则

  1. server:
  2. port: 9999
  3. spring:
  4. application:
  5. name: gateway-server
  6. main:
  7. web-application-type: reactive
  8. cloud:
  9. gateway:
  10. routes:
  11. - id: aaa
  12. uri: lb://api-service1
  13. predicates:
  14. - Path=/product/**
  15. - id: bbb
  16. uri: lb://api-service2
  17. predicates:
  18. - Path=/order/**
  19. filters:
  20. - name: RequestRateLimiter
  21. args:
  22. redis-rate-limiter.replenishRate: 1 #令牌桶每s的填充速度
  23. redis-rate-limiter.burstCapacity: 2 # 令牌桶容量
  24. redis-rate-limiter.requestedTokens: 1
  25. key-resolver: "#{@keyResolver}"
  26. redis:
  27. host: 47.96.11.185
  28. port: 7001
  29. password: qfedu123
  30. database: 0
  31. lettuce:
  32. pool:
  33. max-active: 10
  34. max-wait: 1000
  35. max-idle: 5
  36. min-idle: 3
  37. eureka:
  38. client:
  39. service-url:
  40. defaultZone: http://localhost:8761/eureka