Spring Cloud Gateway 服务网关的部署与使用详细介绍

为什么需要服务网关

传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。有了网关之后,网关作为系统的唯一流量入口,封装内部系统的架构,所有请求都先经过网关,由网关将请求路由到合适的微服务。

使用网关的好处

1)简化客户端的工作。网关将微服务封装起来后,客户端只需同网关交互,而不必调用各个不同服务;
(2)降低函数间的耦合度。 一旦服务接口修改,只需修改网关的路由策略,不必修改每个调用该函数的客户端,从而减少了程序间的耦合性
(3)解放开发人员把精力专注于业务逻辑的实现。由网关统一实现服务路由(灰度与ABTest)、负载均衡、访问控制、流控熔断降级等非业务相关功能,而不需要每个服务 API 实现时都去考虑

Gateway网关的demo

8001端口的服务,然后他有以下两个接口,但是我又不想让别人通过8001端口访问,我想让他通过9527访问怎么办?很简单通过Gateway搭建一个网关服务即可解决该问题。

@RestController
@Slf4j
public class PaymentController {

    @Autowired
    private PaymentMapper paymentMapper;

    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/payment/get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        Payment payment = paymentMapper.selectById(id);
        log.info("*****查询结果:{}", payment);
        if (payment != null) {
            return new CommonResult(200, "查询成功, 服务端口:" + serverPort, payment);
        } else {
            return new CommonResult(444, "没有对应记录,查询ID: " + id + ",服务端口:" + serverPort, null);
        }
    }

    @GetMapping(value = "/payment/lb")
    public String getPaymentLB() {
        return serverPort;
    }
}

接下来搭建一个9527端口的Gateway入门级别的网关服务

1、以下是使用到的核心依赖,一般都会采用聚合工程,由父工程存放dependencyManagement当中的依赖,其他子模块引入使用的组件即可,单纯的练习图省劲的话,创建一个独立可运行的boot项目也可以。

<dependencyManagement>
    <dependencies>
        <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-dependencies</artifactId>
           <version>2.6.8</version>
           <type>pom</type>
           <scope>import</scope>
       </dependency>
        <dependency>
           <groupId>org.springframework.cloud</groupId>
           <artifactId>spring-cloud-dependencies</artifactId>
           <version>2021.0.3</version>
           <type>pom</type>
           <scope>import</scope>
       </dependency>
    </dependencies>
</dependencyManagement>

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

2, 添加配置

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
      - id: payment_routh #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
        uri: http://localhost:8001          #匹配后提供服务的路由地址
        predicates:
        - Path=/payment/get/**         # 断言,路径相匹配的进行路由

      - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
        uri: http://localhost:8001          #匹配后提供服务的路由地址
        predicates:
        - Path=/payment/lb/**         # 断言,路径相匹配的进行路由

3、测试访问:http://localhost:9527/payment/get/1

在这里插入图片描述
在这里插入图片描述

Gateway三大核心概念

Route(路由)

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
请求从外面过来,会先被网关处理,网关会决定该请求会调用哪个微服务,但是这个请求怎么调用呢?所以网关是不是必须要有一个路由器啊,它要可以进行请求的路由转发,具体决定该请求会调用哪个微服务。

Predicate(断言)

是什么

启动网关服务的时候会发现有一排日志,如下所示:
在这里插入图片描述
首先我们会发现一共有12个 RoutePredicateFactorie,他们每个都有自己的应用场景

作用

如果请求与断言相匹配则进行路由,如果不匹配直接404

路由断言规则

在这里插入图片描述

时间作为匹配路由规则

  1. After
    当请求的时间在断言时间之后,将匹配路由
    也就是我们下面配置的时间 2022-03-20T21:02:47.789 之后可以成功匹配路由,之前匹配失败
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - After=2022-03-20T21:02:47.789-07:00[Asia/Shanghai]

当请求的时间在断言时间之后,将匹配路由
也就是我们配置的时间 2022-03-20T21:02:47.789 之后可以成功匹配路由

  1. Before
    与After正好相反

  2. Between
    指定两个时间,用逗号分割,如果请求时间在这两个时间之间,将匹配路由

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Between=2022-03-19T21:02:47.789-07:00[Asia/Shanghai],2022-03-22T21:02:47.789-07:00[Asia/Shanghai]

Cookie作为匹配路由规则

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Cookie=token,123

如果请求cookie中有name为token,且值为123将匹配当前路由
name和value有一个不一样都不能成功路由
name和value都相同 则能成功路由
在这里插入图片描述

在这里插入图片描述

请求头作为匹配路由规则 Header

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Header=token,123

请求对象的请求头中 如果有name为token,且值为123,将匹配当前路由

在这里插入图片描述

如图 测试请求头没有name为token value为123 的请求头信息 则不能匹配路由

在这里插入图片描述

当请求头中有 name为token 值为 123的请求头信息时,能匹配到当前路由

Host作为匹配路由规则 Host

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Host=**.haha.com:81

请求方法作为匹配路由规则 Method

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Method=GET,POST

上面的请求GET和POST请求都能匹配到路由,如果我们换成PUT请求则不能匹配到路由

路径作为匹配路由规则 Path

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Path=/say

上面的案例访问 /say 能匹配到路由
多加一级路径则不能匹配到路由
可以正则 改成 /say/**
此时无论访问 /say 还是 /say/one都能匹配到路由

查询参数作为匹配路由规则 Query

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Query=skuID

如果只写一个参数 则意思为 查询参数有skuID则匹配当前路由
在这里插入图片描述

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8080  # 匹配后提供服务的路由地址
          predicates:
            - Query=skuID,11

如果两个参数,用逗号分割 则意思为 查询参数为skuID ,且值为11 匹配当前路由

注意 两个条件都必须满足 且请求方式与服务请求映射的方式一致

如果skuID 不为 11 也不能匹配到当前路由

权重作为匹配路由规则 Weight

server:
  port: 81
spring:
  cloud:
    gateway:
      routes:   # 配置路由,是一个集合
        - id: apptest1         # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:80  # 匹配后提供服务的路由地址
          predicates:
            - Path=/say/**
            - Weight=group,5
        - id: apptest2          # 路由的ID, 没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8081  # 匹配后提供服务的路由地址
          predicates:
            - Path=/say/**
            - Weight=group,5

Filter(过滤)

过滤器可以在执行方法前和执行方法后进行过滤,所谓的过滤就是可以在请求上加一些操作,例如匹配到路由后可以在请求上添加个请求头,或者参数等等。

Gateway过滤器分为了两种:路由过滤器 和 全局过滤器

路由过滤器

全局过滤器

全局过滤器的作用是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。

自定义全局过滤器(Filter)

Gateway内部有一个接口 名为GlobalFilter,这个就是Gateway的全局过滤器接口,只要在应用中实现此接口后注册为Spring的Bean,它就会就会帮我们将这个实现注册到全局过滤器链条里边去
在这里插入图片描述
测试:

请求中必须要包含uname请求参数,那么才会进行路由转发,否则不会进行路由转发,如下图:
在这里插入图片描述
在这里插入图片描述

过滤器的执行顺序

过滤器会被执行两次,过滤分为pre和post。

pre:请求前调用。
post:响应结果返回时调用,顺序和pre完全相反。

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
在这里插入图片描述
排序规则如下:

每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>