SpringCloud Alibaba【微服务分布式组件---Gateway】

目录

    • 7. SpringCloud 微服务网关Gateway组件
      • 7.1 什么是SpringCloud Gateway
          • 7.1.1 核心概念
          • 7.1.2 工作原理
      • 7.2 SpringCloud Gateway 快速开始
      • 7.3 Gateway---整合 Nacos
      • 7.4 Gateway---内置路由断言工厂
        • 7.4.1 路由断言工厂(Route Predicate Factorys)配置
        • 7.4.2 自定义路由断言工厂
      • 7.5 Gateway---过滤器工厂(Gateway Filter Factory)配置
        • 7.5.1 自定义过滤器工厂
      • 7.6 Gateway---全局过滤器
        • 7.6.1 全局过滤器(Global Filter)配置
      • 7.7 Gateway---请求日志记录&跨域处理
      • 7.8 Gateway---整合sentinel降级
        • Sentinel 流控降级详细配置

上一篇文章《微服务分布式组件—Seata》

7. SpringCloud 微服务网关Gateway组件


当设计多个微服务之后,会出现诸多的问题:

  • 每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很繁冗,我们可以将其抽出来,放到一个统一的地方去管理
  • 如果业务量比较简单的话,这种方式前期是不会有什么问题的,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名,另一个方面是连接数的瓶颈
  • 后期如果需要对微服务进行重构的话,也会变得非常麻烦,需要客户端配合你一起进行改造,比如商品服务,随着业务越来越复杂,后期需要进行拆分多各微服务,这个时候对外提供的服务业需要拆分成多个,同时需要客户端配合你进行改造

上面的这些问题我们可以通过 API 网关来解决

所谓的 API 网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、授权、监控、路由转发等等

SpringCloud Alibaba【微服务分布式组件---Gateway】_第1张图片

7.1 什么是SpringCloud Gateway

网关作为流量入口,常用的功能包括路由转发、权限校验、限流等

  • SpringCloud Gateway 是 SpringCloud 官方推出的第二代网关框架,定位于取代 Neflix Zuul。相比 Zuul 来说,SpringCloud Gateway 提供更优秀的性能,更强大的功能

  • SpringCloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包

  • SpringCloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等

很多地方说 Zuul 是阻塞的,Gateway 是非阻塞的,这么说不严谨。准确地讲,Zuul 1.x 版本是阻塞的,而在 Zuul 2.x 也是基于 Netty,是非阻塞的

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-starter

SpringCloud Gateway 功能特性

  • 基于Spring Framework 5,Project Reactor 和 SpringBoot 2.0 进行构建
  • 动态路由,能够匹配任何请求属性
  • 支持路径重写
  • 集成 SpringCloud 服务发现功能(Nacos、Eruka)
  • 可集成流控降级
  • 可以对路由指定易于编写的 Predicate(断言)和 Filter (过滤器)
7.1.1 核心概念
  • 路由(Route)

路由是网关中最基础的部分,路由信息包括一个 ID、一个目的 URL、一组 Filter 组成。如果断言为真,则说明请求的 URL 和配置的路由匹配

  • 断言(Predicate)

Java8 中的断言函数,SpringCloud Gateway中的断言函数类型是 Spring5.0 框架中的 ServerWebExchange。断言函数允许开发者去定义匹配 Http request 中的任何信息,比如请求头和参数等

  • 过滤器(Filter)

SpringCloud Gateway 中的 Gateway Filter 和 Global Filter。Filter 可以对请求和响应进行处理

7.1.2 工作原理

SpringCloud Alibaba【微服务分布式组件---Gateway】_第2张图片

7.2 SpringCloud Gateway 快速开始

1)创建一个springcloud-gateway-8088模块

2)导入依赖pom.xml

<dependencies>
    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-gatewayartifactId>
    dependency>
dependencies>

3)配置application.yml文件

server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: http://localhost:80  # 需要转发的地址
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer_serv/**
              # http://localhost:8088/consumer_serv/consumer/dept/msg 路由到
              # http://localhost:80/consumer_serv/consumer/dept/msg
          filters:
            # http://localhost:80/consumer/dept/msg
            - StripPrefix=1   # 转发之前,过滤掉第一层前缀 "consumer_serv"

4)测试

  1. 启动 Nacos 服务注册中心
  2. 启动springcloud-provider-dept-8001
  3. 启动springcloud-consumer-dept-80
  4. 启动springcloud-gateway-8088
  5. 访问http://localhost:8088/consumer_serv/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第3张图片

7.3 Gateway—整合 Nacos

1)修改application.yml

server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      discovery:
        locator:
          enabled: true   # 是否启动自动识别nacos服务
    # 配置 Nacos
    nacos:
      discovery:
        # nacos 服务注册地址
        server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
        username: nacos
        password: nacos

2)启动springcloud-gateway-8088

  1. 访问http://localhost:8088/springcloud-consumer-dept/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第4张图片

推荐使用如下配置的application.yml,这样能够更灵活,配置更具体

server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer_serv/**
              # http://localhost:8088/consumer_serv/consumer/dept/msg 路由到
              # http://localhost:80/consumer_serv/consumer/dept/msg
          filters:
            # http://localhost:80/consumer/dept/msg
            - StripPrefix=1   # 转发之前,过滤掉第一层前缀 "consumer_serv"
    # 配置 Nacos
    nacos:
      discovery:
        server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
        username: nacos
        password: nacos

7.4 Gateway—内置路由断言工厂

7.4.1 路由断言工厂(Route Predicate Factorys)配置

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

作用:当请求 gateway 的时候,使用断言对请求进行匹配,如果匹配成功就路由转发,如果匹配失败就返回404

类型:内置

SpringCloud Alibaba【微服务分布式组件---Gateway】_第5张图片

SpringCloud Gateway 断言工厂

SpringCloud Gate 包括许多内置的断言工厂,所有这些断言都与 HTTP 请求的不同属性匹配

  • 基于 Datetime 类型的断言工厂
System.out.println(ZonedDateTime.now());		# 获取区域时间

此类型的断言根据时间做判断,主要有三个:

AfterRoutePredicateFactory:接收一个日期参数,判断请求日期是否晚于指定日期

BeforeRoutePredicateFactory:接收一个日期参数,判断请求日期是否早于指定日期

BetweenRoutePredicateFactory:接收两个日期参数,判断请求日期是否在指定时间段内

AfterRoutePredicateFactory

- After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer/**
              # 访问20201-05-13...之后
            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
#          filters:
#             # http://localhost:80/consumer/dept/msg
#            - StripPrefix=1   # 转发之前,过滤掉第一层前缀 "consumer_serv"
    # 配置 Nacos
    nacos:
      discovery:
        server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
        username: nacos
        password: nacos

接口测试工具:ApiPost

下载客户端地址

  • 基于远程地址的断言工厂

The RemoteAddr Route Predicate Factory:接收一个 IP 地址,判断请求主机地址是否在地址段中

- RemoteAddr=192.168.1.1/24
  • 基于 Cookie 的断言工厂

The Cookie Route Predicate Factory:接收两个参数,cookie 名字和一个人正则表达式,判断请求

- Cookie=chocolate, ch.p
  • 基于 Header 的断言工厂

The Header Route Predicate Factory:接收两个参数,标题名称和正则表达式,判断请求 Header 是否具有给定名称且值与正则表达式匹配

- Header=X-Request-Id,\d+

由于设置响应头不好操作,我们使用ApiPost工具

spring:
  cloud:
    gateway:
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          predicates:
            - Path=/consumer/**
              # 访问20201-05-13...之后
            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
              # 匹配一个或多个数字的正则表达式
            - Header=X-Request-Id,\d+

SpringCloud Alibaba【微服务分布式组件---Gateway】_第6张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第7张图片

  • 基于 Host 的断言工厂

The Host Route Predicate Factory:接收一个参数,主机名模式,判断请求的 Host 是否满足匹配规则

- Host=**.somehost.org,**.anotherhost.org
  • 基于 Method 请求方法的断言工厂

The Method Route Predicate Factory:接受一个参数,判断请求类型是否跟指定的类型匹配

- Method=GET,POST
spring:
  cloud:
    gateway:
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          predicates:
            - Path=/consumer/**
              # 访问20201-05-13...之后
            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
              # 匹配一个或多个数字的正则表达式
            - Header=X-Request-Id,\d+
              # 只允许是GET请求的接口
            - Method=GET

SpringCloud Alibaba【微服务分布式组件---Gateway】_第8张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第9张图片

  • 基于 Path 请求路径的断言工厂

The Path Route Predicate Factory:接受一个参数,判断请求的 URI 部分是否满足路径规则

- Path=/red/{segment},/blue/{segment}
  • 基于 Query 请求参数的断言工厂

The Query Route Predicate Factory:接收两个参数,请求 param 和正则表达式,判断请求是否具有给定名称且值与正则表达式匹配

- Query=green
spring:
  cloud:
    gateway:
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          predicates:
            - Path=/consumer/**
              # 访问20201-05-13...之后
            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
              # 匹配一个或多个数字的正则表达式
            - Header=X-Request-Id,\d+
              # 只允许是GET请求的接口
            - Method=GET
              # 必须要有一个"name"的参数,可以选择该参数的指定的值
            - Query=name,vinjcent|totoro

SpringCloud Alibaba【微服务分布式组件---Gateway】_第10张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第11张图片

  • 基于路由权重的断言工厂

The Weight Route Predicate Factory:接收到 [组名,权重],然后对于同一个组内的路由按照权重转发

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Path=/product/**
        # 代表在group1组中,10次访问有8次路由到 https://weighthigh.org服务下的/product/** 地址
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Path=/product/**
        # 10次访问有2次路由到 https://weightlow.org服务下的/product/** 地址
        - Weight=group1, 2

7.4.2 自定义路由断言工厂

自定义理由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply() 方法。在 apply() 1方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息

实现条件:

  • 必须是 spring 组件 bean
  • 类必须加上 RoutePredicateFactory 作为结尾,由于底层采用的是反射的方式,会根据配置文件中 Query + RoutePredicateFactory 找到自定义的路由断言工厂
  • 必须继承 AbstractRoutePredicateFactory
  • 必须在自定义类中实现静态内部类 AbstractRoutePredicateFactory 声明属性来接受配置文件中对应的断言信息,需要结合 shortcutFieldOrder() 方法进行绑定
  • 通过 apply() 方法进行逻辑判断,true 就是匹配成功,false 就是匹配失败

SpringCloud Alibaba【微服务分布式组件---Gateway】_第12张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第13张图片

测试

  1. 自定义一个类 DivCheckAuthRoutePredicateFactory.class,必须以xxxRoutePredicateFactory结尾
package com.vinjcent.springcloud.config;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * 自定义路由断言工厂
 * 如果匹配的"name"不是vinjcent
 * 那么就会返回404
 */
@Component
public class DivCheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<DivCheckAuthRoutePredicateFactory.Config>  {

    public DivCheckAuthRoutePredicateFactory() {
        super(DivCheckAuthRoutePredicateFactory.Config.class);
    }

    // 绑定 config()
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("name");
    }

    public Predicate<ServerWebExchange> apply(DivCheckAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            public boolean test(ServerWebExchange exchange) {

                if (config.getName().equals("vinjcent")){
                    return true;
                }
                return false;
            }

        };
    }

    // 用于接收配置文件中  断言的信息
    @Validated
    public static class Config {

        private String name;

        public Config(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
  1. 启动springcloud-gateway-8088

SpringCloud Alibaba【微服务分布式组件---Gateway】_第14张图片

  1. 访问http://localhost:8088/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第15张图片

  1. 修改 application.yml

SpringCloud Alibaba【微服务分布式组件---Gateway】_第16张图片

  1. 访问http://localhost:8088/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第17张图片

7.5 Gateway—过滤器工厂(Gateway Filter Factory)配置

Gateway 内置了很多的过滤器工厂,我们可以通过一些过滤器工厂进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等

33个过滤器

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

过滤工厂 作用 参数
AddRequestHeader 为原始请求添加 Header Header 的名称及值
AddRequestParameter 为原始请求添加请求参数 参数名称及值
AddResponseHeader 为原始响应添加 Header Header 的名称及值
DedupeResponseHeader 剔除响应头中重复的值 需要去重的 Header 名称及去重策略
CircuitBreaker 为路由引入 Hystrix的断路器保护 HystrixCommand 的名称
FallbackHeaders 为 fallback Uri 的请求头添加具体的异常信息 Header 的名称
MapRequestHeader 创建一个新的命名标头 ( ),并从传入的 http 请求中从 toHeader 现有的命名标头 ( ) 中提取值 Header 的名称及值,使用","隔开
PrefixPath 为原始请求路径添加前缀 前缀路径
PreserveHostHeader 为请求添加一个 preserveHostHeader = true 的属性,路由过滤器会检查该属性以决定是否要发送原始的 Host
RequestRateLimiter 用于请求限流,限流算法为令牌桶 keyResolver、rateLimiter、statusCode、denyEmptKey、emptyKeyStatus
RedirectTo 将原始请求重定向到指定的 URL http状态码及重定向的 url
RemoveRequestHeader 为原始请求删除某个 Header Header 名称
RemoveResponseHeader 为原始响应删除某个 Header Header 名称
RemoveRequestParameter 为原始请求删除查询参数的名称 - RemoveRequestParameter=red
RequestHeaderSize 请求头(包括键和值)允许的最大数据大小 - RequestHeaderSize=1000B,如果大于1000字节,会发送 431 状态
RewritePath 重写原始的某给请求路径 原始路径正则表达式以及重写后路径的正则表达式
RewriteLocationResponseHeader
RewriteResponseHeader 重写原始响应中的某个 Header Header 名称,值得正则表达式,重写后的值
SaveSession 再转发请求之前,强制执行 WebSession::save 操作
SecureHeaders 为原始响应添加一系列起安全作用的响应头 无,支持修改这些安全响应头的值
SetPath 修改原始的请求路径 修改后的路径
SetRequestHeader 修改原始请求中某个 Header 的值 Header名称,修改后的值
SetResponseHeader 修改原始响应中某个 Header 的值 Header名称,修改后的值
SetStatus 修改原始相应的状态码 HTTP 状态码,可以是数字,也可以是字符串
StripPrefix 用于截断原始请求的路径 使用数字表示要截断的路径数量
Retry 针对不同的响应进行重试 retries、statuses、methods、series
RequestSize 设置允许接收最大请求包的大小,如果请求包大小超过设置的值,则返回 413 Payload Too Large 请求包大小,单位为字节,默认值为 5M
SetRequestHostHeader 设置覆盖主机的请求头 Header host: 主机头名称
Modify a Request Body 在转发之前修改原始请求体内容 修改后的请求体内容
Modify a Response Body 修改原始响应体的内容 修改后的响应内容
Token Relay - TokenRelay=
CacheRequestBody 缓存请求体 - name: CacheRequestBody
Default Filters

AddRequestHeader 测试

  1. springcloud-consumer-dept-80模块controller中添加一个/consumer/header接口
@RestController
@RequestMapping("/consumer")
@SuppressWarnings("all")
public class DeptController {

    @Autowired
    private RestTemplate restTemplate;

    private static final String REST_URL_PREFIX = "http://springcloud-provider-dept";

    @GetMapping("/dept/msg")
    public String getMessage() {

        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/msg", String.class);
    }

    @GetMapping("/header")
    public String getHeader(@RequestHeader("X-Request-color") String color) {

        return color;
    }

}
  1. 修改springcloud-gateway-8088模块下applicatiom.yml文件
server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer/**
#            - DivCheckAuth=vinjcent
#              # 访问20201-05-13...之后
#            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
#              # 匹配一个或多个数字的正则表达式
#            - Header=X-Request-Id,\d+
#              # 只允许是GET请求的接口
#            - Method=GET
#              # 必须要有一个"name"的参数,可以选择该参数的指定的值
#            - Query=name,vinjcent|totoro
          filters:
            - AddRequestHeader=X-Request-color, blue    # 添加请求头
#             # http://localhost:80/consumer/dept/msg
#            - StripPrefix=1   # 转发之前,过滤掉第一层前缀 "consumer_serv"
    # 配置 Nacos
 ......
  1. 启动springcloud-consumer-dept-80
  2. 启动springcloud-gateway-8088
  3. 访问http://localhost:8088/consumer/header

SpringCloud Alibaba【微服务分布式组件---Gateway】_第18张图片

PrefixPath 测试

  1. 修改springcloud-consumer-dept-80模块application.yml文件
server:
  port: 80
  servlet:
    context-path: /vinjcent

spring:
  application:
    name: springcloud-consumer-dept
  # 配置nacos
  cloud:
    nacos:
      # nacos 服务地址
      server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
      discovery:
        username: nacos   # nacos用户名
        password: nacos
        namespace: public   # 分隔开发环境和测试环境使用

# 调用的服务提供方
springcloud-provider-dept:
  ribbon:
    # 复制NacosRule类的引用路径
    NFLoadBalancerRuleClassName: com.vincjent.ribbon.MyRibbon
......

  1. 修改springcloud-gateway-8088模块application.yml文件
server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer/**
#            - DivCheckAuth=vinjcent
#              # 访问20201-05-13...之后
#            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
#              # 匹配一个或多个数字的正则表达式
#            - Header=X-Request-Id,\d+
#              # 只允许是GET请求的接口
#            - Method=GET
#              # 必须要有一个"name"的参数,可以选择该参数的指定的值
#            - Query=name,vinjcent|totoro
          filters:
            - AddRequestHeader=X-Request-color, blue    # 添加请求头
            - PrefixPath=/vinjcent    # 添加前缀,对应微服务需要配置context-path
  1. 启动springcloud-consumer-dept-80
  2. 启动springcloud-gateway-8088
  3. 访问http://localhost:8088/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第19张图片

  1. 访问http://localhost/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第20张图片

  1. 访问http://localhost/vinjcent/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第21张图片

RedirectTo 测试

  1. 修改springcloud-gateway-8088模块application.yml文件
server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer/**
#            - DivCheckAuth=vinjcent
#              # 访问20201-05-13...之后
#            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
#              # 匹配一个或多个数字的正则表达式
#            - Header=X-Request-Id,\d+
#              # 只允许是GET请求的接口
#            - Method=GET
#              # 必须要有一个"name"的参数,可以选择该参数的指定的值
#            - Query=name,vinjcent|totoro
          filters:
            - AddRequestHeader=X-Request-color, blue    # 添加请求头
            - PrefixPath=/vinjcent    # 添加前缀,对应微服务需要配置context-path
            - RedirectTo=302,https://www.baidu.com/   # 302错误重定向到百度

7.5.1 自定义过滤器工厂

  1. springcloud-gateway-8088模块下创建一个DivFilterGatewayFilterFactory.class类,注意后缀必须是 xxxGatewayFilterFactory
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;

@Component
public class DivFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<DivFilterGatewayFilterFactory.Config> {


    public DivFilterGatewayFilterFactory() {
        super(DivFilterGatewayFilterFactory.Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("value");
    }

    public GatewayFilter apply(DivFilterGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

                String name = exchange.getRequest().getQueryParams().getFirst("name");

                if (StringUtils.isNotBlank(name)){
                    if (config.getValue().equals(name)){
                        return chain.filter(exchange);
                    }else {
                        // 返回404
                        exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                        return exchange.getResponse().setComplete();
                    }
                }
                // 正常请求

                return chain.filter(exchange);
            }
        };
    }



    public static class Config {
        String value;

        public Config() {
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        public Config(String value) {
            this.value = value;
        }
    }
}
  1. 修改application.yml文件
server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer/**
#            - DivCheckAuth=vinjcent
#              # 访问20201-05-13...之后
#            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
#              # 匹配一个或多个数字的正则表达式
#            - Header=X-Request-Id,\d+
#              # 只允许是GET请求的接口
#            - Method=GET
#              # 必须要有一个"name"的参数,可以选择该参数的指定的值
#            - Query=name,vinjcent|totoro
          filters:
            - DivFilter=vinjcent	#指定过滤参数可以为null或参数值为"vinjcent"
#            - AddRequestHeader=X-Request-color, blue    # 添加请求头
#            - PrefixPath=/vinjcent    # 添加前缀,对应微服务需要配置context-path
#            - RedirectTo=302,https://www.baidu.com/   # 302错误重定向到百度
#             # http://localhost:80/consumer/dept/msg
#            - StripPrefix=1   # 转发之前,过滤掉第一层前缀 "consumer_serv"
    # 配置 Nacos
......
  1. 启动springcloud-gateway-8088

  2. 访问http://localhost:8088/consumer/dept/msg?name=hhh

SpringCloud Alibaba【微服务分布式组件---Gateway】_第22张图片

  1. 访问http://localhost:8088/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第23张图片

  1. 访问http://localhost:8088/consumer/dept/msg?name=vinjcent

SpringCloud Alibaba【微服务分布式组件---Gateway】_第24张图片

7.6 Gateway—全局过滤器

7.6.1 全局过滤器(Global Filter)配置

SpringCloud Alibaba【微服务分布式组件---Gateway】_第25张图片

局部过滤器和全局过滤器区别:

  • 局部:局部针对某个路由请求
  • 全局:针对所有的路由请求,一旦定义了就会投入使用

GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过,GlobalFilter 会作用于所有路由

LoadBalancerClientFilter

LoadBalancerClientFilter 会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一个RUI),如果该值的 scheme 是 lb,比如:lb://myservice,它将会使用 SpringCloud 的 LoadBalancerClient 来将 myservice 解析成实际的 host 和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内容

自定义全局过滤器

  1. springcloud-gateway-8088模块下创建DivGlobalFilter.class并实现GlobalFilter接口重写filter方法
@Component
public class DivGlobalFilter implements GlobalFilter {

    Logger log = LoggerFactory.getLogger(this.getClass());
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 在控制台打印请求资源路径
        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}
  1. 启动springcloud-gateway-8088并访问
  2. 访问http://localhost:8088/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第26张图片

7.7 Gateway—请求日志记录&跨域处理

Reactor Netty 访问日志

要启动 Reactor Netty 访问日志,请设置 -Dreactor.netty.http.server.accessLogEnabled=true

它必须是 Java 系统属性,而不是 SpringBoot 属性配置

可以将日志记录系统配置为具有单独的访问日志文件。以下示例创建一个 Logback 配置

  • logback.xml
<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
    <file>access_log.logfile>
    <encoder>
        <pattern>%msg%npattern>
    encoder>
appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
    <appender-ref ref="accessLog" />
appender>

<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
    <appender-ref ref="async">appender-ref>
logger>

SpringCloud Alibaba【微服务分布式组件---Gateway】_第27张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第28张图片

  1. 启动springcloud-gateway-8088并访问

  2. 访问http://localhost:8088/consumer/dept/msg

SpringCloud Alibaba【微服务分布式组件---Gateway】_第29张图片

Gateway 跨域配置(CORS Configuration)

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "https://docs.spring.io"
            allowedMethods:
            - GET

使用配置文件方式

  1. springcloud-gateway-8088模块下,添加一个main.html位置于pom.xml同一等级目录下
DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>单选页面title>
    <head>
        <meta charset="utf-8">
        <title>Bootstrap 实例 - 悬停表格title>
        <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js">script>
        <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
        <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js">script>
    head>
    <style>
        .btn-block{
            border: darkgray 2px solid
        }
        .toStore{
            margin-top: 20px;
        }
        .toStore a{
            text-decoration: none;
        }
    style>
<body>
<p class="toStore"><a href="/toStore">点击跳转库存页面a>p>
<form action="/choose" onsubmit="">
<table class="table table-hover">
    <caption>你心中的偶像是?caption>
    <thead>
    <tr>
        <th>名称th>
        <th>单选框th>
    tr>
    thead>
    <tbody>
    <tr>
        <td>Tanmaytd>
        <td><input type="radio" value="Tanmay" name="idol" checked>td>
    tr>
    <tr>
        <td>Sachintd>
        <td><input type="radio" value="Sachin" name="idol">td>
    tr>
    <tr>
        <td>Umatd>
        <td><input type="radio" value="Uma" name="idol">td>
    tr>
    <tr>
        <td>Totorotd>
        <td><input type="radio" value="Totoro" name="idol">td>
    tr>
    <tr>
        <td>Vinjcenttd>
        <td><input type="radio" value="Vinjcent" name="idol">td>
    tr>
    tbody>
table>
    <p>
    <div class="input-group">
        <span class="input-group-addon">年龄span>
        <input type="text" id="age" name="age" class="form-control" placeholder="Say something...">
    div>
    p>
    <p>
    <input type="button" class="btn btn-default btn-lg btn-block" value="点击跨域请求" onclick="getMsg()" />

    p>
form>

body>
<script>

    function getMsg(){
        $.get('http://localhost:8088/consumer/dept/msg',function (data){
            alert(data);
        });
    }

script>

html>
  1. 在 IDEA 中打开该网页并点击按钮请求

SpringCloud Alibaba【微服务分布式组件---Gateway】_第30张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第31张图片

  1. 修改application.yml文件
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':    # 允许跨域访问的资源
            allowedOrigins: "*"    # 跨域允许的来源,如www.vinjcent.com
            allowedMethods:		# 允许请请求的类型	
              - GET
              - POST
  1. 重新启动springcloud-gateway-8088并代开网页请求

SpringCloud Alibaba【微服务分布式组件---Gateway】_第32张图片

通过代码实现

  1. 在模块中创建一个CORSConfiguration.class
package com.vinjcent.springcloud.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;

import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

@Configuration
public class CORSConfiguration {
    
    @Bean
    public CorsWebFilter corsWebFilter() {

        CorsConfiguration config = new CorsConfiguration();
        // 设置所有请求方法都可以访问
        config.addAllowedMethod("*");
        // 设置所有请求源地址都可以访问
        config.addAllowedOrigin("*");
        // 设置所有请求响应头都可以访问
        config.addAllowedHeader("*");

        // 运行访问的资源
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**",config);
        
        return new CorsWebFilter(source);
    
    }
}
  1. 重新启动springcloud-gateway-8088并代开网页请求

7.8 Gateway—整合sentinel降级

网关作为内部系统外的一层屏障,对内起到一定的保护作用,限流便是其中之一. 网关层和限流可以简单地针对不同路由进行限流,也可以针对业务的接口进行限流,或者根据接口的特征分组限流

源码地址:https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81

1)在springcloud-gateway-8088模块下

  1. 添加依赖pom.xml

<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.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloudartifactId>
        <groupId>com.vinjcentgroupId>
        <version>0.0.1-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>gatewayartifactId>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
    properties>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-gatewayartifactId>
        dependency>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-alibaba-sentinel-gatewayartifactId>
        dependency>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
        dependency>
    dependencies>


project>
  1. 添加配置application.yml
server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    # 配置 Nacos
    nacos:
      discovery:
        server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
        username: nacos
        password: nacos
    # 配置Sentinel
    sentinel:
      transport:
        dashboard: 127.0.0.1:8858   # Sentinel控制台地址
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer/**
#            - DivCheckAuth=vinjcent
#              # 访问20201-05-13...之后
#            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
#              # 匹配一个或多个数字的正则表达式
#            - Header=X-Request-Id,\d+
#              # 只允许是GET请求的接口
#            - Method=GET
#              # 必须要有一个"name"的参数,可以选择该参数的指定的值
#            - Query=name,vinjcent|totoro
#          filters:
#            - DivFilter=vinjcent
#            - AddRequestHeader=X-Request-color, blue    # 添加请求头
#            - PrefixPath=/vinjcent    # 添加前缀,对应微服务需要配置context-path
#            - RedirectTo=302,https://www.baidu.com/   # 302错误重定向到百度
#             # http://localhost:80/consumer/dept/msg
#            - StripPrefix=1   # 转发之前,过滤掉第一层前缀 "consumer_serv"
      # 跨域配置
#      globalcors:
#        cors-configurations:
#          '[/**]':    # 允许跨域访问的资源
#            allowedOrigins: "*"    # 跨域允许的来源,如www.vinjcent.com
#            allowedMethods:
#              - GET
#              - POST
  1. 运行 sentinel-dashboard服务

SpringCloud Alibaba【微服务分布式组件---Gateway】_第33张图片

  1. 启动springcloud-gateway-8088
  2. 访问网关中的任意接口http://localhost:8088/consumer/dept/msg,从而使服务注册到我们的 Sentinel 监控服务中
  3. 注意,在自己设置的 Sentinel服务中打开】打开http://localhost:8858/#/dashboard/gateway/flow/api-gateway,并对改网关设置流控规则

SpringCloud Alibaba【微服务分布式组件---Gateway】_第34张图片

  1. 任意快速访问在该网关中的微服务接口

SpringCloud Alibaba【微服务分布式组件---Gateway】_第35张图片

Sentinel 流控降级详细配置

Burst size(最大容许数量)

SpringCloud Alibaba【微服务分布式组件---Gateway】_第36张图片

Client IP(客户端IP)

SpringCloud Alibaba【微服务分布式组件---Gateway】_第37张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第38张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第39张图片

Header(请求头)

SpringCloud Alibaba【微服务分布式组件---Gateway】_第40张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第41张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第42张图片

URL 参数

SpringCloud Alibaba【微服务分布式组件---Gateway】_第43张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第44张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第45张图片

API 分组

SpringCloud Alibaba【微服务分布式组件---Gateway】_第46张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第47张图片

降级规则

SpringCloud Alibaba【微服务分布式组件---Gateway】_第48张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第49张图片

SpringCloud Alibaba【微服务分布式组件---Gateway】_第50张图片

并同时在页面中访问出现异常的接口http://localhost:8088/consumer/exception

自定义异常处理(使用 Java 配置)

  1. springcloud-gateway-8088模块下添加GatewayConfig.class
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class GatewayConfig {

    @PostConstruct
    public void init() {

        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {

            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {

			   // 异常输出	
                System.out.println(throwable);

                Map<String, String> map = new HashMap<>();
                map.put("code",HttpStatus.TOO_MANY_REQUESTS.toString());
                map.put("message","限流了!!!");


                // 设置响应状态码
                return ServerResponse.status(HttpStatus.OK)
                        // 设置相应类型
                        .contentType(MediaType.APPLICATION_JSON)
                        // 设置相应内容
                        .body(BodyInserters.fromValue(map));


            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);

    }

}
  1. 访问任意接口使得网关服务在 Sentinel 监控台显示
  2. 配置该网关接口的流控规则

SpringCloud Alibaba【微服务分布式组件---Gateway】_第51张图片

  1. 快速访问通过该网关的接口

SpringCloud Alibaba【微服务分布式组件---Gateway】_第52张图片

自定义异常处理(使用 application.yml 配置)

server:
  port: 8088
spring:
  application:
    # 服务名称
    name: api-gateway
  cloud:
    # 配置 Nacos
    nacos:
      discovery:
        server-addr: 192.168.159.100:7070,192.168.159.100:7080,192.168.159.100:7090
        username: nacos
        password: nacos
    # 配置Sentinel
    sentinel:
      transport:
        dashboard: 127.0.0.1:8858   # Sentinel控制台地址
      # 配置自定义异常
      scg:
        fallback:
          mode: response
          response-body: "{code:'404',message:'服务出错啦!'}"
    inetutils:
      # 配置忽略的网卡信息
      ignored-interfaces: 'VMware Virtual Ethernet Adapter for VMnet1,VMware Virtual Ethernet Adapter for VMnet8'
    # gateway 配置
    gateway:
      # 路由规则
      routes:
        - id: consumer_route  # 路由的唯一标识,路由到consumer
          uri: lb://springcloud-consumer-dept  # 从nacos中按照服务名获取微服务   lb: 使用nacos中的本地负载均衡策略
          # 断言规则  用于路由规则的匹配
          predicates:
            - Path=/consumer/**
#            - DivCheckAuth=vinjcent
#              # 访问20201-05-13...之后
#            - After=2021-05-13T13:14:57.028+08:00[Asia/Shanghai]
#              # 匹配一个或多个数字的正则表达式
#            - Header=X-Request-Id,\d+
#              # 只允许是GET请求的接口
#            - Method=GET
#              # 必须要有一个"name"的参数,可以选择该参数的指定的值
#            - Query=name,vinjcent|totoro
#          filters:
#            - DivFilter=vinjcent
#            - AddRequestHeader=X-Request-color, blue    # 添加请求头
#            - PrefixPath=/vinjcent    # 添加前缀,对应微服务需要配置context-path
#            - RedirectTo=302,https://www.baidu.com/   # 302错误重定向到百度
#             # http://localhost:80/consumer/dept/msg
#            - StripPrefix=1   # 转发之前,过滤掉第一层前缀 "consumer_serv"
      # 跨域配置
#      globalcors:
#        cors-configurations:
#          '[/**]':    # 允许跨域访问的资源
#            allowedOrigins: "*"    # 跨域允许的来源,如www.vinjcent.com
#            allowedMethods:
#              - GET
#              - POST

使用代码配置断言、异常、访问接口

同样也可以使用代码的方式配置规则,接口为 GatewayxxxRule

package com.vinjcent.springcloud.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Configuration
public class GatewayConfig {

    @PostConstruct
    public void init() {

        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {

            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {


                System.out.println(throwable);

                Map<String, String> map = new HashMap<>();
                map.put("code",HttpStatus.TOO_MANY_REQUESTS.toString());
                map.put("message","限流了!!!");


                // 设置响应状态码
                return ServerResponse.status(HttpStatus.OK)
                        // 设置相应类型
                        .contentType(MediaType.APPLICATION_JSON)
                        // 设置相应内容
                        .body(BodyInserters.fromValue(map));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);

    }

    @PostConstruct
    public void initRules() {

        // 初始化自定义的API
        initCustomizedApis();
        // 初始化网关的流控规则
        initGatewayRules();
        // 自定义限流异常处理器
        initBlockRequestHandler();

    }

    private void initCustomizedApis() {

        HashSet<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api = new ApiDefinition("user_service_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/user/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});

        definitions.add(api);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        // resource: 资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称
        // count: 限流阈值
        // intervalSec: 统计时间窗口,单位是秒,默认是1s
        rules.add(new GatewayFlowRule("order_route")
                .setCount(2)
                .setIntervalSec(1)
        );

        rules.add(new GatewayFlowRule("user_service_api")
                .setCount(2)
                .setIntervalSec(1)
        );

        // 加载网关
        GatewayRuleManager.loadRules(rules);

    }

    private void initBlockRequestHandler() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                HashMap<String, String> result = new HashMap<>();
                result.put("code",String.valueOf(HttpStatus.TOO_MANY_REQUESTS));
                result.put("msg",HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());

                return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(result));
            }
        };

        // 设置自定义异常处理器
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

}

网关高可用

为了保证 Gateway 的高可用性,可以同时启动多个 Gateway 实例进行负载,在 Gateway 的上游使用 Nginx 或者 F5 进行负载转发以达到高可用

SpringCloud Alibaba【微服务分布式组件---Gateway】_第53张图片

下一篇文章《SkyWalking 微服务链路追踪》

你可能感兴趣的:(SpringCloud,spring,cloud,微服务,分布式)