API 网关 —— Gateway简单认识

核心概念

  • Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式
  • 供统一的路由方式,并且还基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。
  • 前端请求都请求到该微服务中,该微服务再负责转发到别的服务中去
  • 和 Zuul 区别:
    • Zuul:拦截单个url规则、默认去除拦截前缀、重复的url规则后面的会将前面覆盖
    • Gateway:拦截多个url规则、默认不去除拦截前缀、重复url规则前面的会将后面覆盖
  • 规则:
    • 断言的条件必须全部满足,才会路由转发
    • 所有的断言规则,必须全部为真,才能够进行请求转发
    • 只要断言为假,就会返回404状态码

简单用法

导入依赖

<!--网关的起步依赖-->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--网关的负载均衡的起步依赖-->
<dependency>
 <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

配置application.yml

spring:
  cloud:
    # Gateway网关配置信息
    gateway:
      # 网关路由配置,包含断言、过滤器、请求转发
      routes:
          # 路由的唯一标识
        - id: toBaidu
          # 断言规则
          predicates:
            # 校验路径拦截规则,当出现相同的拦截路径时,前面的会将后面覆盖           
            - Path: /          
          uri: https://www.baidu.com # 请求转发的url地址
        - id: toGoogle
          predicates:
            # 定义多个路径的拦截规则
            - Path: /, /guonei, /guoji
          uri: https://www.google.com
        - id: toConsumer
          predicates:
            # 拦截规则,可以通过通配符,进行加载
            # 默认不去除拦截前缀:http://localhost:5002/consumer/02  转发还是  http://localhost:8002/consumer/02
            - Path: /consumer/**
          uri: http://localhost:8002

更多应用

实现负载均衡

spring:
  cloud:
    gateway:
      routes:
        - id: toConsumer
          predicates:
            - Path: /consumer/**
          # 负载均衡访问规则
        	# lb 即 load balanced
          # lb://ip+端口
          # lb://微服务名称
          # uri 后面就是与当前服务构成负载均衡的服务
          uri: lb://eureka-client-consumer # uri: http://localhost:8002

更多断言规则

spring:
  cloud:
    gateway:
      routes:
        - id: toConsumer
          # 全部断言规则为真,才可以进行路由访问操作
          # 如果断言规则为假,会范围404页面
          predicates:
            - Path=/consumer/**
            # 以下为常见断言规则:
            # 指定时间之后才允许访问
            - After=2021-12-23T14:44:47.789-07:00[Asia/Shanghai]
            # 指定时间之前才允许访问
            - Before=2021-12-23T14:51:47.789-07:00[Asia/Shanghai]
            # 指定时间之间才允许访问
            - Between=2021-12-23T14:51:47.789-07:00[Asia/Shanghai], 2021-12-23T15:00:47.789-07:00[Asia/Shanghai]
            # 指定Cookie才允许访问
            - Cookie=abc, bcd
            # 指定请求头才允许访问
            - Header=X-Request-Id, abc
            # 指定请求方式才允许访问
            - Method=GET, POST
            # 指定请求参数才允许访问(只能以地址栏的键值对方式进行校验)
            - Query=aaa, bbb
          uri: http://localhost:8002

实现过滤器

spring:
  cloud:
    gateway:
      routes:
        - id: toConsumer
          predicates:
            - Path=/consumer/**, /api/**
          uri: lb://eureka-client-consumer
          # 过滤器工厂(给请求添加以下数据)
          filters:
            # 给请求添加请求头,后面 blue 是值
            - AddRequestHeader=X-Request-Header, blue
            # 给请求添加请求参数
            - AddRequestParameter=key1, value1
            # 给请求添加响应头(可在浏览器中进行查看)
            - AddResponseHeader=ccc, ddd
            # 给请求添加前缀访问路径
            - PrefixPath=/api
            # 去除访问前缀的过滤器,1代表去除1级路径,2代表去除两级路径
            # http://localhost:5002/api/consumer/02 -> http://localhost:5002/consumer/02
            - StripPrefix=1
            # 请求路径重写
            # http://localhost:5002/api/abc/02 -> http://localhost:5002/consumer/02
            - RewritePath=/api/abc/?(?<segment>.*), /consumer/$\{segment}
            # 给请求设置响应码
            - SetStatus=401
          	# 需先引入redis
            # 通过网关和Redis结合,根据令牌桶算法,对请求进行限流操作,只有在令牌桶获取到令牌的请求才允许访问
            # 当请求过多,被限流时,返回的是429的响应码
            - name: RequestRateLimiter
              args:
                # 当令牌生成时,获取的请求会被存储到Redis中,当请求执行完成,Redis会清理掉之前存入的数据
                # 每秒钟令牌生成速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的容量
                redis-rate-limiter.burstCapacity: 2
                # 每次请求获取的令牌数,默认就是1个
                redis-rate-limiter.requestedTokens: 1
                # xxx 为注入容器的一个对象(只能有一个),该对象可设置过滤器的条件
                key-resolver: "#{@xxx}"

以上Redis网关限流需先注入 key-resolver 对象

//yaml中的 key-resolver 修改为 #{@userKeyResolver}
@Bean
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); //请求中的参数必须包含key为 “user”
}

实现自定义全局过滤器

@Slf4j
@Configuration
public class CustomFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("过滤器执行了...");

        //最简单的权限拦截操作
        String token = exchange.getRequest().getQueryParams().getFirst("token");

        if(token == null){
            return errorInfo(exchange,1,"Token校验失败...");
        }

        //放行操作,访问后续的过滤器或访问请求转发到网关操作
        return chain.filter(exchange);
    }

    /**
     * 过滤器的优先级
     *      数字越小,优先级越高
     *          int HIGHEST_PRECEDENCE = -2147483648;
     *          int LOWEST_PRECEDENCE  = 2147483647;
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }

    public Mono<Void> errorInfo(ServerWebExchange exchange, Integer code, String message) {

        //封装返回值结果集
        Map<String, Object> errorMap = new HashMap<>();
        errorMap.put("code", code);
        errorMap.put("message", message);
        errorMap.put("data", null);


        return Mono.defer(() -> {
            byte[] bytes = null;
            try {
                //结果集转换为字节数组
                bytes = new ObjectMapper().writeValueAsBytes(errorMap);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        	//获取 response 对象
            ServerHttpResponse response = exchange.getResponse();
            response.getHeaders().add("Content-Type", MediaType.APPLICATION_PROBLEM_JSON_UTF8.toString());
            
            DataBuffer wrap = response.bufferFactory().wrap(bytes);
            return response.writeWith(Flux.just(wrap));
        });

    }
}

扩展:SpringSession的校验

同一台机器不同微服务所使用的session空间不相同,无法共享数据,此时我们可以使用 SpringSession 来解决

示意图如下:
在这里插入图片描述
在这里插入图片描述
用法:
引入依赖

<!--Redis起步依赖-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--SpringSession起步依赖-->
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
</dependency>

启动类加上注解

//开启SpringSession操作
@EnableRedisHttpSession
public class Application {

此时存取session的数据都是从同一个redis中存取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值