Spring Cloud Alibaba 网关 Gateway 集成(7)

项目的源码地址
Spring Cloud Alibaba 工程搭建(1)
Spring Cloud Alibaba 工程搭建连接数据库(2)
Spring Cloud Alibaba 集成 nacos 以及整合 Ribbon 与 Feign 实现负载调用(3)
Spring Cloud Alibaba Ribbon 负载调用说明(4)
Spring Cloud Alibaba 核心理论 CAP与BASE理论简单理解(5)
Spring Cloud Alibaba Sentinel 集成与限流实战(6)

什么是网关

我们从下面几个点来说明吧:

  1. API Gateway,是系统的唯⼀对外的⼊⼝,介于客户端和服务器端之间的中间层,处理⾮业务功能,提供路由请求、鉴权、监控、缓存、限流等功能。
  2. 统一接入,可以做智能路由,负载均衡,容灾处理,日志埋点等等
  3. 流量监控,限流处理与服务降级
  4. 安全防护,统一的鉴权处理,监控,机器网络隔离

    主流的网关有哪些呢?
  • zuul: 是Netflix开源的微服务网关,和 Eureka,Ribbon,Hystrix 等组件配合使用,依赖组件比较多,性能教差(这个是我百度之后搬过来的,我还没有用过这个 zuul)。
  • nginx+lua:是⼀个⾼性能的HTTP和反向代理服务器,lua 是脚本语⾔,让Nginx执⾏Lua脚本,并且⾼并发、⾮阻塞的处理各种请求
  • SpringCloud Gateway: Spring公司专⻔开发的⽹关,替代 zuul。

AlibabaCloud 全家桶还没对应的⽹关,我们就⽤ SpringCloud官⽅推荐的 Gateway。

Spring Cloud Gateway

spring 官方的说明文档,点击这里看看,官网上面有一句话,是说 Spring Cloud Gateway 是基于 Spring Boot 2.x,Spring WebFlux 和 Project Reactor 构建。

这里引用官方上面的两段文字,是需要我们引起注意的地方

Spring Cloud Gateway 是建立在 Spring Boot 2.x、 Spring WebFlux 和 Project Reactor之上。因此,你所熟悉的许多同步库(例如Spring Data和Spring Security)和模式在你使用Spring Cloud Gateway时可能不适用。如果你不熟悉这些项目,我们建议你在使用Spring Cloud Gateway之前,先阅读它们的文档,熟悉一些新概念。
Spring Cloud Gateway 需要 Spring Boot 和 Spring Webflux 提供的 Netty 运行时。它不能在传统的Servlet容器中工作,也不能以WAR的形式构建。

三个核心的术语

  • Route(路由): 网关的基本构件。它由一个ID、一个目的地URI、一个谓词(Predicate)集合和一个过滤器(Filter)集合定义。如果集合谓词为真,则路由被匹配。

  • Predicate(谓词): 这是一个 Java 8 Function Predicate。输入类型是 Spring Framework ServerWebExchange。这让你可以在HTTP请求中的任何内容上进行匹配,比如header或查询参数。

  • Filter(过滤器): 这些是 GatewayFilter 的实例,已经用特定工厂构建。在这里,你可以在发送下游请求之前或之后修改请求和响应。

这里我们先有一个眼熟,后面我们再具体操作。

工程搭建

新增 api-gateway 模块

这里我们按照之前搭建工程的方式创建一个 demo-api-gateway 的模块出来,具体的搭建方式看这里,搭建的方式是一样的,其实就是新建一个 module,这里就不赘述了。最后搭建完了之后,是这样子的结构:

增加模块内容

pom 文件依赖引入

xml 复制代码
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

启动类

java 复制代码
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

配置文件

这个 routes 节点是一个数组,是可以写多个的

yml 复制代码
server:
  port: 8888

spring:
  application:
    name: api-gateway

  cloud:
    gateway:
      routes: #数组形式
        - id: demo-order  #路由唯一标识
          uri: http://127.0.0.1:9000  #想要转发到的地址
          order: 1 #优先级,数字越小优先级越高
          predicates: #断言 配置哪个路径才转发
            - Path=/demo-order/**
          filters: #过滤器,请求在传递过程中通过过滤器修改
            - StripPrefix=1  #去掉第一层前缀

这里先初步说明下配置的意思:

  • 我们请求的地址如果是: http://localhost:8888/demo-order/api/v1/video_order/list ,这个是我们网关的地址,
  • 访问网关之后,会转发到 这个地址上面去 http://localhost:9000/demo-order/api/v1/video_order/list 访问我们订单服务。

测试下

访问下:http://localhost:8888/demo-order/api/v1/video_order/list

然后我们再访问下:http://localhost:9000/api/v1/video_order/list

OK了,我们初步的测试就算通过了,那么我们就接着向下看把,

连接Nacos

在 pom 文件中增加 nacos 依赖

xml 复制代码
 <!--添加 nacos 客户端-->
 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
 </dependency>

增加启动类开启支持

java 复制代码
@EnableDiscoveryClient

修改配置文件

注意看下下面增加的部分

  • nacos.discovery.server-addr
  • gateway.routes.id.uri:这里使用了 lb://demo-order 负载的调用方式
  • gateway.locator.enabled: 开启网关拉取 nacos 服务
yml 复制代码
server:
  port: 8888

spring:
  application:
    name: api-gateway

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848

    gateway:
      routes: #数组形式
        - id: order-service  #路由唯一标识
          #uri: http://127.0.0.1:8000  #想要转发到的地址

          uri: lb://demo-order  #从nocas进行转发

          order: 1 #优先级,数字越小优先级越高
          predicates: #断言 配置哪个路径才转发
            - Path=/order-server/**
          filters: #过滤器,请求在传递过程中通过过滤器修改
            - StripPrefix=1  #去掉第一层前缀

      discovery:
        locator:
          enabled: true  #开启网关拉取nacos的服务

logging:
  level:
    org.springframework.cloud.gateway: DEBUG

我们去订单服务里面增加一个接口,方便于测试

java 复制代码
@RequestMapping("gateway")
private Map gateway(HttpServletRequest httpRequest) {
    String serverInfo = httpRequest.getServerName() + ":"+ httpRequest.getServerPort();
    return Map.of("title", "测试返回数据", "name", "返回名称", "serverInfo", serverInfo);
}


测试下

首先我们启动两个订单服务

然后我们看下 nacos 的面板,可以看到相关的服务都在上面了

开始请求:http://localhost:8888/order-server/api/v1/video_order/gateway

再次请求下:http://localhost:8888/order-server/api/v1/video_order/gateway

断言、过滤器实战

网关的配置项回顾

路由:是网关的基本单元,由ID、URI、一组Predicate、一组Filter组成,根据Predicate进行匹配转发

route组成部分

id:路由的ID
uri:匹配路由的转发地址
predicates:配置该路由的断言,通过 PredicateDefinition 类进行接收配置。
order:路由的优先级,数字越小,优先级越高。

交互流程

  • 客户端向Spring Cloud Gateway发出请求
  • 如果网关处理程序映射确定请求与路由匹配
  • 则将其发送到网关Web处理程序
  • 通过特定过滤器链运行,前置处理-后置处理

断言

什么是Gateway路由断言 ?

  • Predicate 来源于Java8,接受输入参数,返回一个布尔值结果
  • Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则
  • 转发的判断条件,SpringCloud Gateway支持多种方式,常见如:Path、Query、Method、Header等
  • 支持多个Predicate请求的转发是必须满足所有的Predicate后才可以进行路由转发

内置路由断言都是 RoutePredicateFactory 接口实现类,我们一起看下:

我们可以去访问下这里,具体的说明这里也有Route Predicate(路由谓词)工厂,我们这里就选几个出来看看

Before

比如,我们现在有一个需求接口需要在指定时间进行下线,过后不可以在被访问。那么我们可以使用 Before Gateway 内置的路由接口定时下线实战。

Before 路由谓词工厂只需要一个参数,即 datetime(这是一个java ZonedDateTime)。这个谓词匹配发生在指定 datetime 之前的请求。

yaml 复制代码
predicates:
  - Before=2024-05-11T01:01:01.000+08:00

那么修改下把:

测试访问下,现在是可以访问的:

然后我们再修改下:

yaml 复制代码
Before=2024-05-09T01:01:01.000+08:00
Query

query 参数表示再请求的时候一定要带上的字段,如果没有这个字段就是非法请求

访问请求

http://localhost:8888/order-server/api/v1/video_order/gateway

http://localhost:8888/order-server/api/v1/video_order/gateway?source=alipay

Filter 过滤器

这里我们再看下这个图,可以看到我们的 Filter,是一层层的过滤然后访问到 proxied service ,然后再一层层的经过 filter 返回

过滤器生命周期

PRE: 这种过滤器在请求被路由之前调用,一般用于鉴权、限流等

POST:这种过滤器在路由到微服务以后执行,一般用于修改响应结果,比如增加header信息、打点结果日志

网关过滤器分类

  • 局部过滤器GatewayFilter:应用在某个路由上,每个过滤器工厂都对应一个实现类,并且这些类的名称必须以GatewayFilterFactory 结尾

  • 全局过滤器:作用全部路由上

局部过滤器

内置很多局部过滤器,顶级接口 GatewayFilterFactory

全局过滤器

内置很多全局过滤器,顶级接⼝ GlobalFilter

自定义网关过滤器

第一步 :先增加一个自定义的过滤器

java 复制代码
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


@Component
public class UserGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //写业务逻辑
        String token = exchange.getRequest().getHeaders().getFirst("token");

        //TODO 根据业务开发对应的鉴权规则, JWT

        if(StringUtils.isBlank(token)){
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }


        //继续往下执行
        return chain.filter(exchange);
    }

    //数字越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

第二步: 接着重启服务

访问测试下:http://localhost:8888/order-server/api/v1/video_order/gateway?source=alipay
通过 postMan 访问,可以请求到数据

好了,我们总算是搞定了这个 Gateway 了,其实这个更多的就是配置,没有啥其他代码部分的东西。先理解配置部分东西,方便与工作中使用。

相关推荐
珍珠是蚌的眼泪17 小时前
微服务_入门2
网关·微服务·gateway·远程调用·feign
铁板鱿鱼14018 小时前
统一网关--gateway(仅供自己参考)
gateway
bug菌¹18 小时前
滚雪球学SpringCloud[4.2讲]: Zuul:Netflix API Gateway详解
spring·spring cloud·gateway
炸裂狸花猫2 天前
Kubernetes从零到精通(12-Ingress、Gateway API)
容器·kubernetes·gateway
云来喜4 天前
关于Spring Cloud Gateway中 Filters的理解
java·spring boot·gateway
小小小小关同学4 天前
【Gateway】网关服务快速上手
java·开发语言·gateway
小小小小关同学4 天前
【Gateway】Gateway Filter Factories
网络·gateway
两仪式quq4 天前
服务网关Gateway快速入门
服务器·网络·gateway
szc17676 天前
Gateway学习笔记
笔记·学习·gateway
huaqianzkh9 天前
什么是API网关(API Gateway)?
架构·gateway