API 网关的定义

网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。

API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。

API 网关的职能

  • 请求接入:作为所有API接口服务请求的接入点
  • 业务聚合:作为所有后端业务服务的聚合点
  • 中介策略:实现安全、验证、路由、过滤、流控等策略
  • 统一管理:对所有API服务和策略进行统一管理

使用Gateway的优势

Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty 实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。
Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用
比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。

Gateway的使用

调用流程分析

在这里插入图片描述

注意事项:

Predicate(断言)要先于Filter调用,断言为真才能进行路由匹配,而Filter过滤器又分为两种,局部过滤器Gateway Filter和全局过滤器Global Filter.在全局过滤器中可以设置全局过滤器的优先级别,所以两者的调用顺序是由设置的优先级别决定的.

配置格式:

spring:
  cloud:
    gateway:
      routes:
       - id: route01 #路由id,自己指定一个唯一值即可
         uri: lb://sca-provider #用于获取指定服务名的服务列表
         predicates: #断言(谓词):定义请求规则
            #断言内容
         filters: #网关过滤器,用于对谓词中的内容进行判断分析以及处理
            #过滤器内容


几个重要概念

Route(路由):

这是网关的基本构建块.它由一个id,一个目标uri,一组断言和一组过滤器定义.如果断言为真,则路由匹配.

Predicate(断言):

输入类型是一个ServerwebExchange.我们可以使用它来匹配来自HTTP请求的任何内容,例如headers或参数.说白了Predicate就是事先定义了一组匹配规则,方便让请求过来找到对应的 Route 进行处理.

过滤器(filter):

Gateway中的Filter分为两种类型,分别是Gateway Filter和Global Filter.过滤器Filter将会对请求和响应进行修改处理.

综合使用进行匹配

添加测试方法

        @GetMapping("/provider/name")
        public String name(String name){
            return "my name is "+name;
        }

创建配置文件

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      routes: #配置网关路由规则
        - id: route01 #路由id,自己指定一个唯一值即可
          #uri: http://localhost:8082/ #网关帮我们转发的url
          uri: lb://sca-provider #用于获取指定服务名的服务列表
          predicates: #断言(谓词):定义请求规则
            #- Path=/nacos/provider/echo/** #请求路径定义,此路径对应uri中的资源,多层目录
            - Path=/nacos/provider/name
            - After=2021-08-23T15:00:00.545+08:00[Asia/Shanghai]
            - Header=X-Request-Id, \d+ #添加请求头信息
            - Query=pageSize,\d+ #添加请求参数信息
          filters: #网关过滤器,用于对谓词中的内容进行判断分析以及处理
            - StripPrefix=1 #转发之前去掉path中的第一层路径,例如nacos
            - AddRequestParameter=name,zzll #自动添加请求参数
            - AddRequestHeader=X-Request-Foo,Bar #自动添加请求头
      discovery:
        locator:
          enabled: true #开启基于服务名获取服务实例的功能(基于服务名创建路由)
#配置白名单
white:
  prefix: /nacos #path路径以nacos开头的才能进行下一步操作(白名单)

创建全局过滤器

package com.jt.config;

import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;

@Component//交给spring管理
public class AuthGatewayFilter implements GlobalFilter, Ordered {
    /**
     * @param exchange 基于此对象可以获取请求和响应对象
     * @param chain 这个对象指向了一个过滤链(这个链中有多个过滤器)
     * @return
     */
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1.获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //2.获取请求数据
        String path = request.getURI().getPath();
        System.out.println(path);
        String username = request.getQueryParams().getFirst("username");
        System.out.println("pageSize="+request.getQueryParams().getFirst("pageSize"));
        //3.对请求数据进行处理
        System.out.println("whitePrefix="+whitePrefix);
        if (!path.startsWith(whitePrefix)){//判断path路径是否以nacos开头,不是则返回一个JSON串
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.BAD_GATEWAY);
            Map<String,Object> map = new HashMap<String, Object>();
            map.put("message", "request failure");
            map.put("status", 502);
            Gson gson = new Gson();//需先添加gson依赖
            String jsonStr = gson.toJson(map);//将map对象转为JSON字符串
            byte[] bytes = jsonStr.getBytes();
            DataBuffer dataBuffer=response.bufferFactory().wrap(bytes);
            return response.writeWith(Mono.just(dataBuffer));
        }

        //4.返回响应结果
        return chain.filter(exchange);
    }

    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;//设置全局过滤器的优先级别为最高级,则先经过全局过滤器
    }
    @Value("${white.prefix}")//获取配置文件中的白名单
    private String whitePrefix;
}

使用postman进行测试

成功案例:

 

失败案例:

如果将path路径的nacos改为其它,如sentinel,即

- Path=/sentinel/provider/name

重新启动后再次测试:

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐