浅聊一下微服务的网关模块

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

大家好呀!今天来跟大家好好聊聊微服务里超重要的 “守门人”——Spring Cloud Gateway。不管是做微服务架构设计,还是日常开发调试,网关都是绕不开的环节,这篇就从基础到实战,把 Gateway 的核心知识点讲明白,新手也能轻松看懂~

一、先搞懂:为什么需要网关?Gateway 是什么?

首先得明确一个问题:微服务集群里,为啥非要整个网关?
其实网关就像小区的保安亭,所有外部请求都得先经过它,再转发到对应的微服务。没有网关的话,每个微服务都要自己处理权限校验、流量控制,既冗余又难维护,而网关能把这些 “通用活儿” 全揽下来,让微服务专心搞业务。

再说说 Spring Cloud Gateway 本身:它是 Spring Cloud 家族的新成员,基于 Spring 5.0、Spring Boot 2.0 和响应式编程技术开发,性能比老款的 Zuul(基于 Servlet 阻塞编程)好不少,现在基本是微服务网关的首选。

二、Gateway 核心功能:这 3 件事它最擅长

网关不是花架子,核心就干 3 件关键事儿,咱们一个个说:

  1. 权限控制:比如用户没登录、没权限,网关直接拦住请求,不用让请求跑到微服务里再被打回来,效率高多了。
  2. 路由 + 负载均衡:你访问/product-serv/xxx,网关知道要转发到 “商品微服务”;要是商品微服务有 3 个实例,网关还能自动帮你分配请求,实现负载均衡(不用自己再搭 Nginx 那套)。
  3. 限流:比如秒杀活动时请求突然暴涨,网关能按微服务能承受的速度 “放行”,避免微服务被冲垮,保障系统稳定。

放个简单的架构图,大家一看就懂:

注册中心/配置中心(Nacos)
       ↓(微服务注册、网关读配置)
微服务A(连数据库)←→ Feign ←→ Gateway网关 ←→ Feign → 微服务B(连数据库)
                                   ↓
                               微服务C(连数据库)

三、实战上手:Gateway 快速搭建(超详细步骤)

光说不练假把式,咱们一步步搭建一个基础网关,实现路由功能。

3.1 第一步:创建 Gateway 服务,引入依赖

先建一个 Spring Boot 模块(比如叫api-gateway),然后在pom.xml里加两个关键依赖:网关本身和 Nacos 服务发现(后面负载均衡要用)。

<!-- 网关核心依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos服务发现依赖(网关要从Nacos找微服务) -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

3.2 第二步:写配置文件,搞定路由规则

接下来写application.yml,核心是配置 “服务基本信息” 和 “路由规则”。这里分两个版本,新手先看基础版,再看高级版。

基础版(固定地址转发)

适合刚开始测试,直接指定微服务的 IP + 端口:

yaml

server:
  port: 7000  # 网关的端口
spring:
  application:
    name: api-gateway  # 网关服务名
  cloud:
    gateway:
      routes:  # 路由数组:一个路由对应一个微服务的转发规则
        - id: product_route  # 路由唯一标识(随便起,但不能重复)
          uri: http://localhost:8081/  # 转发目标地址(商品微服务的地址)
          order: 1  # 优先级:数字越小越先执行
          predicates:  # 断言:满足这个条件才转发
            - Path=/product-serv/**  # 路径规则:请求路径以/product-serv开头就转发
          filters:  # 过滤器:转发前处理请求
            - StripPrefix=1  # 去掉路径的第一级(比如请求/product-serv/product/19,转发后变成/product/19)
高级版(从 Nacos 拉取微服务,支持负载均衡)

实际项目里微服务地址会变,而且可能多实例,这时候就用lb://服务名的方式,让网关从 Nacos 找微服务,自动做负载均衡:

server:
  port: 7000
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # Nacos的地址
    gateway:
      discovery:
        locator:
          enabled: true  # 允许网关发现Nacos里的微服务
      routes:
        - id: product_route
          uri: lb://service-product  # lb=负载均衡,service-product是微服务在Nacos的名字
          order: 1
          predicates:
            - Path=/product-serv/**
          filters:
            - StripPrefix=1

还要注意:启动类上要加@EnableDiscoveryClient注解,让网关能注册到 Nacos~

3.3 第三步:测试一下

启动网关、Nacos 和商品微服务(假设商品微服务端口 8081,有个接口/product/19):
访问http://localhost:7000/product-serv/product/19,网关会自动转发到http://localhost:8081/product/19,能拿到结果就说明搭建成功啦!

四、断言工厂:网关怎么判断 “该转发给谁”?

前面配置里的predicates: - Path=/product-serv/**,其实是用了 “路径断言”,背后是 Spring 提供的 “断言工厂” 在工作。简单说:咱们写的断言字符串,会被断言工厂转换成判断逻辑,只有满足逻辑的请求才会被转发。

Spring Cloud Gateway 提供了十几种断言工厂,常用的列个表,大家按需选:

断言工厂(对应配置关键词) 作用 示例
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(Cookie 名 chocolate,值含 ch.p)
Header 请求必须包含指定 Header - Header=X-Request-Id, \d+(Header 名 X-Request-Id,值是数字)
Host 请求必须访问指定域名 - Host=.somehost.org,.anotherhost.org
Method 只转发指定请求方式(GET/POST 等) - Method=GET,POST
Path 路径匹配(最常用) - Path=/red/{segment},/blue/**
Query 请求必须包含指定参数 - Query=name(必须有 name 参数);- Query=name,Jack(name 参数值是 Jack)
RemoteAddr 请求 IP 在指定范围 - RemoteAddr=192.168.1.1/24

五、过滤器工厂:请求转发前后能做啥?

网关除了转发请求,还能在转发前 / 后修改请求 / 响应,这就靠 “过滤器工厂”。比如加个请求头、删个响应头,或者做限流,都能用现成的过滤器工厂。

5.1 常用过滤器工厂

Spring 提供了 31 种过滤器工厂,挑几个常用的说说:

过滤器工厂(配置关键词) 作用
AddRequestHeader 给请求加一个 Header
RemoveRequestHeader 从请求里删一个 Header
AddResponseHeader 给响应加一个 Header
RemoveResponseHeader 从响应里删一个 Header
RequestRateLimiter 对请求限流(防止接口被刷爆)

5.2 实战:给请求加个 Header

比如想让所有发往商品微服务的请求,都带上msg: wake up!的请求头,修改application.ymlfilters即可:

routes:
  - id: product_route
    uri: lb://service-product
    order: 1
    predicates:
      - Path=/product-serv/**
    filters:
      - StripPrefix=1
      - AddRequestHeader=msg, wake up!  # 加请求头

5.3 默认过滤器:对所有路由生效

如果想让某个过滤器对所有路由都生效(比如所有请求都加同一个 Header),不用每个路由都写一遍,用default-filters配置即可:

spring:
  cloud:
    gateway:
      default-filters:  # 默认过滤器,所有路由都生效
        - AddRequestHeader=msg, wake up!
      routes:
        - id: product_route
          # 其他配置不变...

六、全局过滤器:自定义业务逻辑(比如权限校验)

前面的过滤器都是 “现成的”,只能干固定的活儿。但实际项目里,经常需要自定义逻辑(比如判断用户是否登录、有没有权限),这时候就需要 “全局过滤器”—— 自己写代码实现,想干啥就干啥。

6.1 全局过滤器 vs 普通过滤器

  • 普通过滤器(GatewayFilter):靠配置生效,逻辑固定,只能针对特定路由。
  • 全局过滤器(GlobalFilter):自己写代码,对所有路由生效,逻辑灵活(比如登录校验、权限判断)。

6.2 实战:自定义全局过滤器(权限校验)

需求:所有请求必须带authorization参数,且值为admin,否则拦截(返回 401 未授权)。

步骤 1:写过滤器类

实现GlobalFilter接口,加@Component让 Spring 扫描到,再用@Order指定优先级(数字越小越先执行):

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
@Order(-1)  // 优先级:-1比默认的高,先执行
public class AuthorizeFilter implements GlobalFilter {

    // 核心逻辑:处理请求,决定放行还是拦截
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. 获取请求参数(从URL里拿)
        var params = exchange.getRequest().getQueryParams();
        // 2. 拿到authorization参数的值
        String auth = params.getFirst("authorization");
        
        // 3. 校验:如果参数是admin,就放行
        if ("admin".equals(auth)) {
            // 放行:把请求交给下一个过滤器
            return chain.filter(exchange);
        }
        
        // 4. 拦截:返回401未授权
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        // 结束请求处理
        return exchange.getResponse().setComplete();
    }
}
步骤 2:测试
  • 访问http://localhost:7000/product-serv/product/19?authorization=admin:正常放行,返回结果。
  • 访问http://localhost:7000/product-serv/product/19(没带参数),或参数值不是 admin:返回 401,被拦截。

6.3 过滤器执行顺序

请求进入网关后,会碰到 3 类过滤器:默认过滤器(default-filters)、路由过滤器(routes 里的 filters)、全局过滤器(GlobalFilter)。它们的执行顺序按以下规则:

  1. 所有过滤器都有order值,order越小,越先执行。
  2. 全局过滤器的order自己指定(@Order 注解)。
  3. 默认过滤器和路由过滤器的order由 Spring 自动分配,按配置顺序从 1 开始递增。
  4. order相同:默认过滤器 > 路由过滤器 > 全局过滤器。

七、跨域问题:网关怎么解决?

做前后端分离项目时,肯定会碰到 “跨域”—— 浏览器出于安全考虑,禁止前端访问不同域名 / 端口的接口。比如前端跑在localhost:8090,网关跑在localhost:7000,直接请求就会被浏览器拦截。

7.1 先懂同源策略

浏览器的 “同源” 指 3 个相同:协议相同(比如都是 http)、域名相同(比如都是www.xxx.com)、端口相同(比如都是 80)。只要有一个不同,就是跨域。

7.2 网关解决跨域:配置 CORS

不用在每个微服务里配跨域,网关统一配就行。修改application.yml,加全局跨域配置:

spring:
  cloud:
    gateway:
      globalcors:  # 全局跨域配置
        add-to-simple-url-handler-mapping: true  # 解决OPTIONS请求被拦截问题
        corsConfigurations:
          '[/**]':  # 对所有路径生效
            allowedOrigins:  # 允许哪些前端域名访问
              - "http://localhost:8090"
            allowedMethods:  # 允许的请求方式
              - "GET"
              - "POST"
              - "PUT"
              - "DELETE"
              - "OPTIONS"
            allowedHeaders: "*"  # 允许前端带任何Header
            allowCredentials: true  # 允许前端带Cookie
            maxAge: 360000  # 跨域校验结果的有效期(1小时,避免频繁校验)

配完重启网关,前端再请求就不会有跨域问题了~

总结

本篇把 Spring Cloud Gateway 的核心知识点都覆盖了:从 “为什么需要网关”,到搭建、路由、断言、过滤器、全局逻辑、跨域解决,每个点都带了实战代码,新手跟着做就能上手。实际项目里,网关还能结合限流(比如用 Redis 实现)、日志收集等功能,后续有机会再跟大家深入聊~

如果有疑问,欢迎评论区交流呀!


网站公告

今日签到

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