Spring Cloud Gateway 网关(五)

目录

[一 概念引入](#一 概念引入)

[二 具体使用](#二 具体使用)

[1 首先创建一个网关模块](#1 首先创建一个网关模块)

[2 启动类](#2 启动类)

[3 配置类](#3 配置类)

[4 对应方法的修改](#4 对应方法的修改)

[5 展示借助81端口进行转发控制](#5 展示借助81端口进行转发控制)

[6 断言规则​编辑](#6 断言规则编辑)

[三 过滤器](#三 过滤器)

[1 将前置的请求参数给过滤掉,降低繁琐程度。](#1 将前置的请求参数给过滤掉,降低繁琐程度。)

[2 默认过滤器](#2 默认过滤器)

[3 全局过滤器](#3 全局过滤器)

[4 自定义过滤器工厂](#4 自定义过滤器工厂)

[5 全局跨域问题](#5 全局跨域问题)


一 概念引入

Spring Cloud Gateway :: Spring Cloud Gateway

二 具体使用

1 当前主流更多使用的是Reactive Server ,而ServerMVC是老版本的网关。

前景引入:

LoadBalancer:服务内部调用时的负载均衡。

Gateway 负载均衡:系统对外统一入口的负载均衡,内部也会用 LoadBalancer。

复制代码
        <!--   添加nacos注册中心     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--   添加网关的依赖     -->
        <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>

实现:

1 首先创建一个网关模块

2 启动类

java 复制代码
package com.ax.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class GatewayMainApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMainApplication.class, args);
    }
}

3 配置类

借助的就是81端口的网关进行映射

java 复制代码
spring:
  profiles:
    include: route
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
server:
  port: 81
XML 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order
          predicates:
            - Path=/api/order/**
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**
        - id: last-route
          uri: https://cn.bing.com/
          predicates:
            - Path=/**

4 对应方法的修改

需要以规范的格式开头(加上api/order或者api/product)

java 复制代码
package com.ax.product.controller;

import com.ax.product.bean.Product;
import com.ax.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;

@Slf4j
@RequestMapping("/api/product")
@RestController
public class ProductController {

    @Autowired
    private ProductService productService;

    // 获取商品信息
    @GetMapping("/product/{id}")
    public Product getProduct(@PathVariable("id") Long id) {
        log.info("getProduct:{}", id);
        return productService.getProductById(id);
    }
}

对应的远程调用,这里有一个语法版本兼容问题不能在类的头部写@RequestMapping

java 复制代码
package com.ax.order.feign;

import com.ax.order.feign.fallback.ProductFeignClientFallback;
import com.ax.product.bean.Product;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(name = "service-product", fallback = ProductFeignClientFallback.class)
public interface ProductFeignClient {


    /**
     * 测试FeignClient
     *
     * @param id
     */
    //mvc注解两套使用逻辑
    //标注在Controller上,为接收请求
    //标注在FeignClient上,为发送请求
    @GetMapping("/api/product/product/{id}")
    Product getProductById(@PathVariable("id") Long id);

    //如果调用自己其他服务的api直接将其方法复制过来即可,下面这个就是从product当中复制过来的
    //    @GetMapping("/product/{id}")
    //    Product getProduct(@PathVariable("id") Long id);
}

5 展示借助81端口进行转发控制

6 断言规则

三 过滤器

1 将前置的请求参数给过滤掉,降低繁琐程度。

**举例说明:**路径重写(此时就可以将原先的@RequestMapping当中的参数给去除掉)

XML 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order
          predicates:
            - Path=/api/order/**
          order: 0
          filters:
            - RewritePath=/api/order/(?<segment>.*), /$\{segment}
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**
          order: 1
          filters:
            - RewritePath=/api/product/(?<segment>.*), /$\{segment}
        - id: last-route
          uri: https://cn.bing.com
          predicates:
            - Path=/**
          order: 2

实现目的,就是说如果请求的前缀固定含有某些内容,我们就可以借助这个过滤器将这些请求前缀给过滤掉,比如说访问路径http://localhost:81/api/product/product/1

但是后端接收就可以按照 http://localhost:81/product/1来进行接收,(暂时感觉)也就简化了一个@RequestMapping的注解

2 默认过滤器

使用 default-filters 可以全局加响应头

XML 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order
          predicates:
            - Path=/api/order/**
          order: 0
          filters:
            - RewritePath=/api/order/(?<segment>.*), /$\{segment}
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**
          order: 1
          filters:
            - RewritePath=/api/product/(?<segment>.*), /$\{segment}
        - id: last-route
          uri: https://cn.bing.com
          predicates:
            - Path=/**
          order: 2
      default-filters:
        - AddResponseHeader=X-Response-Abc, 123

携带了一个响应头参数

3 全局过滤器

代码展示:

java 复制代码
package com.ax.gateway.filter;


import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
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;

@Slf4j
@Component
public class RtGlobalFilter implements GlobalFilter, Ordered {
    /**
     * 全局过滤器
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().toString();
        long startTime = System.currentTimeMillis(); // 记录开始时间

        log.info("请求开始: path={}, startTime={}", path, startTime);

        return chain.filter(exchange)
                .doFinally(signalType -> {
                    long endTime = System.currentTimeMillis(); // 记录结束时间
                    long duration = endTime - startTime;      // 计算耗时
                    log.info("请求结束: path={}, endTime={}, 耗时: {}ms", path, endTime, duration);
                });
    }

    /**
     * 优先级
     *
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

示例:

4 自定义过滤器工厂

自定义过滤器工厂(拦截器的名称也有特殊要求)Spring Cloud Gateway 要求自定义过滤器工厂的类名必须以 GatewayFilterFactory 结尾。

java 复制代码
package com.ax.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
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.UUID;

@Component
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                //每次响应之前添加一个一次性令牌

                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    ServerHttpResponse response = exchange.getResponse();
                    HttpHeaders headers = response.getHeaders();
                    String value = config.getValue();
                    if ("uuid".equalsIgnoreCase(value)) {
                        value = UUID.randomUUID().toString();
                    }
                    if ("jwt".equalsIgnoreCase(value)) {
                        value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY" +
                                "3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE3NTY1NDA" +
                                "xMTksImFkbWluIjp0cnVlLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iX" +
                                "SwiZW1haWwiOiJqb2huLmRvZUBleGFtcGxlLmNvbSJ9.SflKxwRJSMeKKF" +
                                "2QT4fwpMeJf36POk6yJV_adQssw5c";
                    }
                    headers.add(config.getName(), value);
                }));

            }
        };
    }
}

配置文件(OnceToken....)

XML 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order
          predicates:
            - Path=/api/order/**
          order: 0
          filters:
            - RewritePath=/api/order/(?<segment>.*), /$\{segment}
            - OnceToken=X-Response-Token,uuid
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**
          order: 1
          filters:
            - RewritePath=/api/product/(?<segment>.*), /$\{segment}
        - id: last-route
          uri: https://cn.bing.com
          predicates:
            - Path=/**
          order: 2
      default-filters:
        - AddResponseHeader=X-Response-Abc, 123

结果展示

5 全局跨域问题

全局跨域(CORS)问题 主要是与前端请求不同源的后端接口时,如何解决跨域问题(Cross-Origin Resource Sharing, CORS)。Spring Cloud Gateway 作为网关,它通常会充当微服务之间的流量调度中心,并需要解决跨域请求的问题,确保前端可以安全地与后端进行交互。

代码实现:

XML 复制代码
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origin-patterns: '*'
            allowed-headers: '*'
            allowed-methods: '*'
      routes:
        - id: order-route
          uri: lb://service-order
          predicates:
            - Path=/api/order/**
          order: 0
          filters:
            - RewritePath=/api/order/(?<segment>.*), /$\{segment}
            - OnceToken=X-Response-Token,uuid
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**
          order: 1
          filters:
            - RewritePath=/api/product/(?<segment>.*), /$\{segment}
        - id: last-route
          uri: https://cn.bing.com
          predicates:
            - Path=/**
          order: 2
      default-filters:
        - AddResponseHeader=X-Response-Abc, 123

结果展示:

补充:

如何理解网关的负载均衡与微服务之间的负载均衡

第一个是客户端与网关之间的请求

第二个是各个微服务应用之间的请求

可以梳理为 两层负载均衡

  1. 第一层:网关层 LB(入口层)

    • 作用于所有外部请求

    • 只管把流量合理分配到下游微服务实例

  2. 第二层:服务调用层 LB(内部层)

    • 作用于服务之间的 RPC 调用

    • 解决微服务内部调用的流量分配问题

微服务之间的调用可以不过网关,但是如果微服务之间的调用也想借助网关,那么可以在远程调用的微服务名称的指定时,将名称修改为对应的网关服务名称。

相关推荐
小莞尔7 小时前
【51单片机】【protues仿真】基于51单片机音乐盒(8首歌曲)系统
c语言·开发语言·单片机·嵌入式硬件·51单片机
星期天要睡觉7 小时前
(纯新手教学)计算机视觉(opencv)实战十二——模板匹配(cv2.matchTemplate)
开发语言·python·opencv·计算机视觉
码农小C8 小时前
idea2025.1.5安装+pj
java·开发语言·apache
David爱编程8 小时前
synchronized 全解析:从用法到底层原理的全面剖析
java·后端
yzx9910138 小时前
Java视觉跟踪入门:使用OpenCV实现实时对象追踪
java·开发语言·人工智能·opencv
sheji34168 小时前
【开题答辩全过程】以 基于php的校园兼职求职网站为例,包含答辩的问题和答案
开发语言·php
中科三方8 小时前
政府网站IPv6检测怎么做?检测指标有哪些?
开发语言·php
ytadpole8 小时前
揭密设计模式:像搭乐高一样构建功能的装饰器模式
java