# 08.微服务-网关

# 1.Spring Cloud Gateway 的特征

# 1.基本介绍

在微服务架构中,我们有几十甚至上百个服务。如果让前端或客户端直接去调用这些服务,不仅需要记住每一个服务的 IP 和端口,还会面临跨域、鉴权、日志、限流等一系列复杂问题。怎么办?这时候,我们就需要一个“统一收发室”——也就是网关。以下是他的特征

  1. 基于异步非阻塞模型:底层基于 Spring WebFlux、Netty 和 Reactor 构建。这意味它不走传统的 Servlet 容器(Tomcat),而是使用少量的线程处理大量的并发请求,性能极高。
  2. 动态路由:能够根据请求的特征(路径、请求头、参数等)动态地将请求转发到不同的微服务。而且支持动态配置,无需重启网关。
  3. 内置强大的断言和过滤器:开箱即用,且支持自定义。
  4. 集成电路熔断器:可以无缝对接 Resilience4j 等熔断降级组件。
  5. 限流支持:内置 Redis 限流算法,保护后端微服务不被大流量压垮。

# 2.核心概念

# 1. Route(路由)

路由是构建网关的基本模块,可以理解为一条转发的规则。它由一个 ID、一个目标 URI、一组 Predicate 和一组 Filter 组成。

# 2. Predicate(断言)

断言相当于“条件判断”。它匹配 HTTP 请求中的所有内容(比如请求路径、请求头、Cookie、请求参数等)。如果请求符合断言的条件,就说明匹配到了这条路由。
类比:收费站的ETC识别系统,识别车牌是不是符合免费条件。

# 3. Filter(过滤器)

过滤器可以在请求被转发到下游微服务之前(前置 Filter)或之后(后置 Filter),对请求或响应进行修改和处理。比如添加请求头、统一鉴权、记录日志等。
类比:收费站的保安,在车开过去之前检查一下后备箱,或者发一张通行卡。


# 2.实战

1.引入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
            <exclusions>
                <!-- 必须排除web,否则启动冲突报错 -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <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-loadbalancer</artifactId>
        </dependency>
    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

2.配置

2.1普通配置

server:
  port: 8099
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      #路由规则
      routes:
       - id: order-route #路由唯一标识,路由到order
         uri: http://localhost:8081 #路由地址
         #断言规则 用于路由规则的匹配
         predicates:
           - Path=/order-serv/** #有order-serv就会被路由到指定地址
             # http://localhost:8099/order-serv/order/add 路由到
             # http://localhost:8081/order-serv/order/add
         filters:
           - StripPrefix=1 #去掉路径中的第一个路径参数(order-serv)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

2.2nacos配置

server:
  port: 8099
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      #路由规则
      routes:
       - id: order-route #路由唯一标识,路由到order
         uri: lb://order-service #路由地址
         #断言规则 用于路由规则的匹配
         predicates:
           - Path=/order-serv/** #有order-serv就会被路由到指定地址
             # http://localhost:8099/order-serv/order/add 路由到
             # http://localhost:8081/order-serv/order/add
         filters:
           - StripPrefix=1 #去掉路径中的第一个路径参数(order-serv)
    nacos:
      server-addr: 47.94.9.59:8848
      discovery: # 服务发现必须配置账号密码
        username: nacos
        password: 2eb4e10f
        namespace: public  # 确保和你的微服务在同一命名空间
      config:
        username: nacos
        password: 2eb4e10f
        namespace: public   # 与 order-service 保持一致
        group: DEFAULT_GROUP
        import-check:
          enabled: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 3.路由断言工厂

# 1.内置断言工厂

名称 说明 示例
After 匹配某个时间点之后的请求 - After=2037-01-20T17:42:47.789-07:00[America/Denver]
Before 匹配某个时间点之前的请求 - Before=2031-04-13T15:14:47.433+08:00[Asia/Shanghai]
Between 匹配某两个时间点之间的请求 - Between=2037-01-20T17:42:47.789-07:00[America/Denver], 2037-01-21T17:42:47.789-07:00[America/Denver]
Cookie 请求必须包含指定 Cookie - Cookie=chocolate, ch.p
Header 请求必须包含指定请求头 - Header=X-Request-Id, \d+
Host 请求必须访问指定域名 - Host=**.somehost.org,**.anotherhost.org
Method 请求方式必须为指定方法 - Method=GET,POST
Path 请求路径必须匹配指定规则 - Path=/red/{segment},/blue/**
Query 请求参数必须包含指定参数 - Query=name, Jack- Query=name
RemoteAddr 请求来源 IP 必须在指定网段 - RemoteAddr=192.168.1.1/24
Weight 按权重分配路由流量 - Weight=group1, 2
server:
  port: 8099
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      #路由规则
      routes:
       - id: order-route #路由唯一标识,路由到order
         uri: lb://order-service #路由地址
         #断言规则 用于路由规则的匹配
         predicates:
           - After=2026-01-20T17:42:47.789-07:00[Asia/Shanghai]
           - Path=/order/** #有order-serv就会被路由到指定地址
             # http://localhost:8099/order-serv/order/add 路由到
             # http://localhost:8081/order-serv/order/add
         ##filters:
           #- StripPrefix=1 #去掉路径中的第一个路径参数(order-serv)
    nacos:
      server-addr: 47.94.9.59:8848
      discovery: # 服务发现必须配置账号密码
        username: nacos
        password: 2eb4e10f
        namespace: public  # 确保和你的微服务在同一命名空间
      config:
        username: nacos
        password: 2eb4e10f
        namespace: public   # 与 order-service 保持一致
        group: DEFAULT_GROUP
        import-check:
          enabled: false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 2.自定义断言工厂

必须带RoutePredicateFactory后缀

Config为内部类,用来定义属性,apply方法中写逻辑

package com.xy.gateway.predicates;

import jakarta.validation.constraints.NotEmpty;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.cloud.gateway.handler.predicate.QueryRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {


    public CheckAuthRoutePredicateFactory() {
        super(CheckAuthRoutePredicateFactory.Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("name");
    }

    public Predicate<ServerWebExchange> apply(final CheckAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                if (config.getName().equals("admin")){
                    return true;
                }
                return false;
            }
        };

    }

    @Validated
    public static class Config {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 4.过滤器

# 1.内部局部过滤器

过滤器名称 作用说明 使用示例
StripPrefix 截取路径前缀,去掉第 N 层路径 - StripPrefix=1
PrefixPath 给请求路径统一拼接前缀 - PrefixPath=/api
RewritePath 重写请求路径,正则替换 - RewritePath=/user/(?<path>.*), /$\{path\}
RedirectTo 3xx 重定向到指定地址 - RedirectTo=302, https://www.baidu.com
SetPath 直接替换完整请求路径 - SetPath=/new/{segment}
SetRequestHeader 设置 / 覆盖请求头 - SetRequestHeader=token, 123456
AddRequestHeader 追加请求头(不覆盖) - AddRequestHeader=X-App, gateway
RemoveRequestHeader 删除指定请求头 - RemoveRequestHeader=Cookie
SetResponseHeader 设置响应头 - SetResponseHeader=Server, gateway-server
AddResponseHeader 追加响应头 - AddResponseHeader=X-Source, gateway
RemoveResponseHeader 删除响应头 - RemoveResponseHeader=X-Powered-By
SetStatus 修改返回 HTTP 状态码 - SetStatus=401
RequestRateLimiter 限流过滤器(令牌桶) - name: RequestRateLimiter
CircuitBreaker 熔断降级(整合 Resilience4j) - name: CircuitBreaker
Retry 请求失败自动重试 - name: Retry
RequestSize 限制请求体最大大小 - name: RequestSize, args: maxSize=5MB
DefaultHttpHeaders 统一添加默认响应头 自动全局生效,无需手动配置

# 2.局部自定义过滤器

package com.xy.gateway.filters;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {


    public CheckAuthGatewayFilterFactory() {
        super(CheckAuthGatewayFilterFactory.Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("value");
    }

    public GatewayFilter apply(final CheckAuthGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if (config.getValue().equals("admin")){
                    return chain.filter(exchange);
                }else {
                    exchange.getResponse().setStatusCode(HttpStatusCode.valueOf(401));
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }



    public static class Config {
        private String value;

        public Config() {
        }

        public String getValue() {
            return this.value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

# 3.全局自定义过滤器

//@Order(-1)
@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //1、获取请求参数
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> params = request.getQueryParams();
        //2、获取参数中的authorization参数
        String auth = params.getFirst("authorization");
        //3、判断参数值是否等于 admin
        if("admin".equals(auth)){
            //4、是,放行
            return chain.filter(exchange);
        }
        //5、否,拦截
        //5.1设置状态码
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        //5.2拦截请求
        return exchange.getResponse().setComplete();
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 5.跨域

什么是跨域?为什么要由网关解决?

当一个请求的协议、域名、端口三者之间任意一个不同,就是跨域。比如前端跑在 http://localhost:8080,而你的网关在 http://localhost:8099,浏览器出于安全限制,默认会拦截这种请求 。

解决原则统一在网关层解决跨域,下游微服务不要配置!
因为网关是所有流量的唯一入口,在这里配置一次就能解决所有微服务的跨域问题。如果在每个微服务里都配,既繁琐又容易出错。

# 网关跨域配置(两种方式)

Spring Cloud Gateway 基于 WebFlux,提供了非常方便的跨域配置。

# 方式一:配置文件方式(推荐,简单直观)

在 application.yml 中添加 globalcors 配置:

spring:
  cloud:
    gateway:
      globalcors:
        # 跨域配置项
        cors-configurations:
          # 匹配所有请求路径
          '[/**]':
            # 允许的源(前端地址)。注意:如果允许携带Cookie,不能写 "*",必须写具体地址
            allowedOrigins: 
              - "http://localhost:8080"
              - "http://www.yourdomain.com"
            # 允许的请求方法
            allowedMethods:
              - GET
              - POST
              - PUT
              - DELETE
              - OPTIONS
            # 允许的请求头
            allowedHeaders: "*"
            # 是否允许携带Cookie等凭证
            allowCredentials: true
            # 预检请求的有效期,单位秒(避免频繁发送OPTIONS请求)
            maxAge: 3600
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 方式二:Java 代码配置(适合动态配置场景)

如果你需要根据数据库或配置中心动态生成允许的跨域域名,可以使用代码注入 CorsWebFilter

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();

        // 推荐使用 addAllowedOriginPattern,它兼容 allowCredentials=true 的情况
        config.addAllowedOriginPattern("*"); 
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        config.setAllowCredentials(true);
        config.setMaxAge(3600L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 6.整合sentinel

1.引入依赖

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
    </dependencies>
1
2
3
4
5
6
7
8
9

2.配置yml文件

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:9090
1
2
3
4
5

最近更新: 9/19/2026, 1:27:08 PM
编程NOTE   |