Spring Cloud Gateway 网关(五)

发布于:2025-09-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

目录

一 概念引入

二 具体使用

1 首先创建一个网关模块

2 启动类

3 配置类

4 对应方法的修改

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

6 断言规则​编辑

三 过滤器

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

2 默认过滤器

3 全局过滤器

4 自定义过滤器工厂

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 启动类

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端口的网关进行映射

spring:
  profiles:
    include: route
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
server:
  port: 81
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)

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

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当中的参数给去除掉)

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 可以全局加响应头

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 全局过滤器

代码展示:

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 结尾。

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....)

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 作为网关,它通常会充当微服务之间的流量调度中心,并需要解决跨域请求的问题,确保前端可以安全地与后端进行交互。

代码实现:

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 调用

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

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


网站公告

今日签到

点亮在社区的每一天
去签到