一文带你吃透微服务守门神SpringCloud-GateWay

一文带你吃透微服务守门神SpringCloud-GateWay

  • GateWay简单介绍
    • Gateway和Zuul的对比:
    • gateWay的几个核心概念
  • 入门案例
  • 网关层负载均衡
  • gateWay 工作原理
    • 三大组件:高级工作原理图(左) 粗
    • 工作流程:
  • 网关路由断言工厂(路由规则)
    • path路由规则:
    • After路由规则:
    • 其他路由规则
  • 网关过滤器
    • 分类
    • PrefixPath 过滤器(局部)
    • 去除前缀过滤器(局部)
    • 自定义过滤器(重要)
  • 自定义全局过滤器
  • 过滤器优先级
  • 网关降级
  • 网关限流
    • 令牌桶:
    • 网关令牌桶限流(使用redis实现)

GateWay简单介绍

一句话:网关是微服务架构系统的守门神 网关本身是一个微服务,是所有微服务的入口。鉴权、限流、路由、监控【监控所有方法执行调用时间】
一文带你吃透微服务守门神SpringCloud-GateWay_第1张图片

Gateway和Zuul的对比:

1.gateway是zuul的代替者。并不是zuul有什么大问题 而是后面zuul宣布闭源了 所以springcloud自己做了一个gateway后来就不再集成zuul了

2.Zuul是基于servlet阻塞式的并且不支持任何长连接 gateway是使用Reactor和springboot开发的 使用非阻塞式的API 性能要优于zuul

gateWay的几个核心概念

非常重要!

必须要对这几个概念熟悉了 才能分析他的高级特性以及功能

1. route/路由:路由是网关的最基本组成,由一个路由 id、一个目标地址 url,一组断言工厂及一组
filter组成。若断言为 true,则请求将经由 filter 被路由到目标 url。 一句话理解 就是定义一条游戏规则

2. predicate断言:断言即一个条件判断,根据当前的 http 请求进行指定规则的匹配,比如说 http
请求头,请求时间等。只有当匹配上规则时,断言才为 true,此时请求才会被直接路由到目标地址
(目标服务器),或先路由到某过滤器链,经过过程器链的层层处理后,再路由到相应的目标地址
(目标服务器)。一句话理解 就是定义一个游戏规则中的多个小规则

3. filter过滤器:对请求进行处理的逻辑部分。当请求的断言为 true 时,会被路由到设置好的过滤
器,以对请求进行处理。例如,可以为请求添加一个请求头,或添加一个请求参数,或对请求URI
进行修改等。总之,就是对请求进行处理。 一句话理解 对请求做过滤

入门案例

需求:访问网关服务跳转到百度(静态路由)

1.创建工程并且加入依赖

<?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.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-alibaba-sxw-parent</artifactId>
        <groupId>com.sxw</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>03-gateway-simple</artifactId>

    <properties>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- actuator依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <!-- spring-cloud的依赖-->
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- spring-cloud-alibaba的依赖 -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

2.配置文件

server:
  port: 9000

spring:
  cloud:
    gateway:
      #添加一组路由
      routes:
          #路由id 唯一标识/名字
        - id: route_to_baidu
          #路由到的目标地址
          uri: http://www.baidu.com
          #断言 也就是规则 我需要定义什么样的请求被被这个路由拦截
          predicates:
            - Path=/**

3.测试
一文带你吃透微服务守门神SpringCloud-GateWay_第2张图片
4.这是使用配置文件配置 也可以使用java类代码来配置

//区别在于可以直接声明好 不需要在配置文件上修改
@SpringBootApplication
public class ApiConfigGateWayApplication {

    public static void main(String[] args) {
        SpringApplication.run(ApiConfigGateWayApplication.class, args);
    }

    @Bean
    //使用的是构建者模式+java8 lambda的语法来编写 优雅简单
    public RouteLocator locator(RouteLocatorBuilder builder){
        return builder.routes().route(predicateSpec -> predicateSpec
                .path("/**")
                .uri("https://www.baidu.com")
                .id("api_config_route_to_baidu"))
                .build();
    }
}

网关层负载均衡

需求:我的服务组成了集群 那么网关支持负载均衡吗? 当然支持
一文带你吃透微服务守门神SpringCloud-GateWay_第3张图片
1.pom依赖

小疑问 为什么需要加入注册中心的依赖?
因为网关本身也是一个微服务 那么也可以将他注册到配置中心
负载均衡 顾名思义 用过feign的同学就知道 有点像域名找服务一样

<!-- 注册中心支持 其他依赖省略... 这里使用nacos作注册中心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2.配置文件(同样可以使用java配置类的方式进行配置)

这里主要关注这个路由规则
uri这个参数:lb://provider
lb:是网关自有的协议 至于为什么叫lb 因为源码已经写死 感兴趣的可以自己查阅ribbon中的实现
provoder:这个其实就是服务名字 这个服务的集群不管有几个 服务名是不变的
这样就可以通过nacos找到这个名字背后的集群地址 通过某种负载均衡机制找到具体某一台 然后进行请求转发

erver:
  port: 9002

spring:
  application:
    name: sxw-gateway-ribbon
  #注册中心配置
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
  #开启与discoveryClient的整合
    gateway:
      discovery:
        locator:
          enable: true
      #路由配置
      routes:
        - id: ribbon_route
          uri: lb://provider
          predicates:
            - Path=/provider/depart/**

3.修改均衡策略 随机负载均衡策略

@Bean
public IRule loadBalanceRule(){
    return new RandomRule();
}

4.启动提供者服务provider集群
一文带你吃透微服务守门神SpringCloud-GateWay_第4张图片

5.验证
···
http://localhost:9002/provider/depart/get/122
···

gateWay 工作原理

一文带你吃透微服务守门神SpringCloud-GateWay_第5张图片

三大组件:高级工作原理图(左) 粗

网关处理器映射器组件 HandlerMapping
网关WEB处理器组件Web Hnadler
网关过滤器组件Filter**

工作流程:

组装网关上下文->循环遍历所有的Mapper获取Handler->匹配路由信息,如果断言失败就返回->进入Filter过滤链条调用链->前置过滤器->自定义过滤器->后置过滤器

网关路由断言工厂(路由规则)

path路由规则:

含义:对这个路径进行拦截
如 - Path=/info/** 请求地址:http://localhost:9000/info/hello 那么就会被path_showinfo_route这个路由拦截转发到http://localhost:8088这个服务
也可以理解 访问 http://localhost:9000/info/hello 等同于访问 http://localhost:8088/info/hello

1.添加两组路由策略

server:
  port: 9000

spring:
  cloud:
    gateway:
      #配置断言工厂1 路由到http://localhost:8999 这个服务
      routes:
        - id: path_consumer_route
          uri: http://localhost:8999
          predicates:
            - Path=/consumer/depart/**
        #配置断言工厂2 路由到http://localhost:8088 这个服务
        - id: path_showinfo_route
          uri: http://localhost:8088
          predicates:
            - Path=/info/**

2.测试

测试1:http://localhost:9000/consumer/depart/get/1
测试2:http://localhost:9000/info/hello

After路由规则:

一句话 配置的时间要比现在的时间小才能够访问 格式固定
如下:这样是不能访问的

配置文件

server:
  port: 9000

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: http://www.baidu.com
          predicates:
            - After=2022-01-20T17:42:47.789-07:00[Asia/Shanghai]

其他路由规则

举一反三:基本的玩法就是配置 根据你的业务需要进行配置即可 不需要全部背熟 常用的还有如下这几种

  • Before
  • Between
  • Cookie
  • Header
  • Host
  • Method

网关过滤器

分类

  • 局部过滤器:应用于单个路由策略上,其功能仅对路由断言为 true 的请求起作用
  • 全局过滤器:应用于所有路由策略上,其功能就像前面的配置文件中设置的默认 Filter

PrefixPath 过滤器(局部)

任何请求都会进行拦截 并在请求地址上增加/info然后将请求转发到目标地址
localhot:9999/findById?id=1 添加之后: localhost:8088/info/findById?id=1
有啥用?保护我们的接口地址 防止被人恶意攻击

spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: http://localhost:8088
          predicates:
            - Path=/**
          filters:
            - PrefixPath=/info

去除前缀过滤器(局部)

举一反三
localhost:9999/user/findById?id=1 去除之后 localhot:9999/findById?id=1

自定义过滤器(重要)

当内置的过滤器不满足业务需要时 当然也支持自定义的过滤器
比如支付有支付的独立网关,我们可以要求,所有的服务进入支付的相关服务之前需要有支付系统颁发的权限token,此时就可以在支付网关做这样一个校验支付token权限的事情。
需求:往请求头添加一个参数

1.编写自定义filter

public class  CustomerFilter implements GatewayFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.构建改变后的request对象
        ServerHttpRequest request = exchange.getRequest().mutate().header("X-Request-customer", "customer").build();
        //2.将改变的request对象置入exchange对象中
        ServerWebExchange webExchange = exchange.mutate().request(request).build();
        //3.设置修改后的exchange对象
        return chain.filter(webExchange);
    }
}

2.配置自定义过滤器生效

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder){
    return builder.routes().route(predicateSpec -> predicateSpec
            .path("/**")
            .filters(gfs-> gfs.filter(new CustomerFilter()))
            .uri("http://localhost:8088")
            .id("updateRequest"))
            .build();
}

3.另外一个服务(转发过去的服务)http://localhost:8088

@RestController()
@RequestMapping("/info")
public class InfoController {

    @GetMapping("/hello")
    public String hello(HttpServletRequest request){
        String header = request.getHeader("X-Request-customer");
        return "new header:" + header;
    }
}

4.测试
一文带你吃透微服务守门神SpringCloud-GateWay_第6张图片

自定义全局过滤器

模拟鉴权 重要!
全局过滤器 针对所有的路由都生效 只需要交由spring容器管理即可
需求:判断当前请求是否携带了token 如果没有就返回错误信息 (实际开发应校验该token是否合法 也就是这个token存不存在 是否过期以及包含的用户是否合法)

1.定义全局过滤器

@Component
public class GlobalFilter implements org.springframework.cloud.gateway.filter.GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求中的token
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        //校验是否存在
        if (StringUtils.isEmpty(token)){
            //如果不存在 返回对应的错误码
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        //如果存在则放行请求
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        //最高级别 返回的int值越大 优先级越高 spring-core的组件
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

2.定义一个路由规则 因为是全局的 不需要指定参数filter

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder){
    return builder.routes().route(predicateSpec -> predicateSpec
            .path("/**")
            .uri("http://localhost:8088")
            .id("global_filter_route"))
            .build();
}

3.测试
测试1不带 token访问:http://localhost:9004/info/hello
一文带你吃透微服务守门神SpringCloud-GateWay_第7张图片
测试2:带token访问http://localhost:9004/info/hello?token=222
一文带你吃透微服务守门神SpringCloud-GateWay_第8张图片

过滤器优先级

值越大优先级越高的原则
一文带你吃透微服务守门神SpringCloud-GateWay_第9张图片

网关降级

当我们路由到的服务不可用时 一样会报错 此时我们期望 网关可以实现降级 为什么需要降级? 给用户友好提示 不要一堆报错信息 看不懂!

1.添加依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>

2.定义降级处理器controller

@RestController
public class FallbackController {

    @RequestMapping("fallback")
    public Map<String,String> fallback(){
        Map<String,String> map = new HashMap<>();
        map.put("code","200");
        map.put("msg","服务降级");
        map.put("data",null);
        return map;
    }
}

3.编写降级配置(filter)-针对一个路由

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder){
    return builder.routes().route(predicateSpec -> predicateSpec
            .path("/**")
            .filters(fs->fs.circuitBreaker(config -> {
                //配置降级
                config.setName("myCircuitBreaker");
                config.setFallbackUri("forward:/fallback");
            }))
            .uri("http://localhost:8088")
            .id("global_filter_route"))
            .build();
}

4.测试针对一个路由降级 把提供者服务下线http://localhost:9004/info/hello?token=222
在这里插入图片描述
5.配置全局路由熔断 配置文件

spring:
  cloud:
    gateway:
      default-filters:
        - name: CircuitBreaker
         args:
           name: myCircuitBreaker
           fallbackUri: forward:/fallback 
      routes: 
        - id: test_filter 
          uri: http://localhost:8088 
          predicates: 
            - Path=/**

网关限流

针对一些恶意攻击 为了保护网关后面的服务 就可以将一些无效的请求拦截掉 这样的做法也是非常必要的

使用redis 令牌桶

令牌桶:

关键:1.桶(放redis 放令牌 放一定数量的令牌) 2.速率

含义:如果需要执行请求 需要去桶里拿到令牌 才能放行 否则被拒绝并返回

一文带你吃透微服务守门神SpringCloud-GateWay_第10张图片

网关令牌桶限流(使用redis实现)

1.依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

2.配置文件

spring:
  redis:
    host: localhost
    port: 6379
  cloud:
    gateway:
      routes:
        - id: requestRateLimiter_filter
          uri: http://localhost:8088
          predicates:
            - Path=/info/**
          filters:
            - name: RequestRateLimiter
              args:
                #  key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#
                key-resolver: "#{@keyResolver}"
                #  replenishRate:令牌桶每秒填充平均速率。
                redis-rate-limiter.replenishRate: 2
                #  burstCapacity:令牌桶总容量。
                redis-rate-limiter.burstCapacity: 5

3.配置类

Bean
//配置令牌同算法key:将主机名作为限流key
public KeyResolver keyResolver(){
    return exchange -> Mono.just(exchange
            .getRequest()
            .getRemoteAddress()
            .getHostName());
}

4.测试
一文带你吃透微服务守门神SpringCloud-GateWay_第11张图片
一文带你吃透微服务守门神SpringCloud-GateWay_第12张图片

你可能感兴趣的:(java高级开发,网关,java,spring)