概述
多服务架构在接入层暴露出的问题,本文给出解法的全貌:网关。先把「为什么引入、引入后放哪、工程里长什么样」讲清楚,具体搭建和鉴权代码留到后续文章展开。
纲要
- 架构问题回顾:项目多服务架构的三个接入层痛点
- 网关是什么:路由 + 过滤器两大核心能力
- 网关的三大职责:统一入口/路由、统一鉴权、统一跨域/限流/日志
- 网关在项目架构中的位置:接入层落点
- 工程落点:实读
mf-gateway模块(pom / bootstrap.yml / 路由规则) - Spring Cloud Gateway 的角色:响应式 Netty,与 MVC 应用的差异
- 统一鉴权预告:
TokenInterceptor的去留问题 - 常见误解澄清
架构问题回顾
前文在架构问题分析中铺过三个痛点,这里快速收拢,因为它们正好对应网关的三项职责:
| 痛点 | 现状 | 后果 |
|---|---|---|
| 多后端地址 | C 端 App 直连 mf-app-server,管理端 Vue 直连 mf-admin | 前端维护多套地址,服务一多就散架 |
| 鉴权重复 | 每个服务各自写 JWT 校验(项目用拦截器 TokenInterceptor) | 同一份校验逻辑拷 N 份,改规则要全量回归 |
| 跨域分散 | CORS 配置散落在各服务 | 管理端 Vue 和 App 两边口径不一致时,排查成本高 |
这三个问题的共同点:它们都是「接入层」的事,却被迫摊派给了每个业务服务。把接入层的职责从各服务里抽出来、集中到一处,这个「一处」就是网关。
网关是什么:路由 + 过滤器
网关(Gateway)处在所有客户端请求的必经之路上:请求先到网关,网关按路由配置把请求转发给后端的具体服务。它有两项核心能力:
- 路由转发:按路径、请求头等条件(谓词,Predicate)决定请求去哪个服务,转发前还能改写路径(Filter)。
- 过滤器:请求转发前后都可以被过滤器拦截,借这个位置可以挂授权、限流、登录校验、日志记录这类横切逻辑。
用一张时序图看请求的走向:
关键收益在过滤器上:功能加在网关一次,所有后端服务自动获得。比如在网关做了 token 统一校验,mf-app-server 和 mf-admin 就都不用再各自校验了,这正是「接入层职责上收」的含义。
网关的三大职责
把上面的两个能力和项目的问题对齐,得到三大职责:
| 职责 | 解决的问题 | 项目里的体现 |
|---|---|---|
| 统一入口 / 路由 | 前端维护多个后端地址 | 前端只认识网关地址 ip:8888,按路径前缀 /app/**、/admin/** 分流 |
| 统一鉴权 | 每个服务重复写 JWT 校验 | JWT 校验在网关做一次,后端服务不再重复鉴权 |
| 统一跨域 / 限流 / 日志 | CORS、限流、日志分散在各服务 | CORS 在网关配一次,全服务生效 |
第二项值得多说两句。项目此前用 TokenInterceptor 做过鉴权,那是在每个服务内部用 Spring MVC 拦截器做统一身份鉴权,「统一」只是统一到了「一个服务内部的所有接口」,服务和服务之间仍然是重复的。引入网关后,鉴权位置整体上移:从「每个服务的拦截器」移到「所有服务的共同入口」。同样叫统一鉴权,粒度完全不同。
网关的位置
对照项目的分层图(表现层 → 应用层 → 域服务层),网关落在哪里一目了然:应用层的最前面,即「接入层」。
三个层次各干各的:网关管「进来」,mf-app-server / mf-admin 管业务编排,Dubbo 域服务管数据。注意网关不做业务逻辑,它是交通枢纽,不是处理车间。
工程落点:实读 mf-gateway
看 09 章终态工程的 mf-gateway 模块,结构非常轻:
mf
├── mf-gateway
│ ├── pom.xml
│ └── src/main
│ ├── java/com/mf/gateway
│ │ ├── GatewayApplication.java # 引导类
│ │ ├── config/CorsConfig.java # 跨域支持
│ │ └── filters/AuthFilter.java # 统一鉴权过滤器(后篇展开)
│ └── resources/bootstrap.yml
├── mf-app-server
├── mf-admin
├── mf-dubbo(db / mongo / interface)
├── mf-model / mf-commons / mf-autoconfig
└── pom.xml
pom:三件套依赖
<dependencies>
<!-- 网关核心:基于响应式 WebFlux/Netty 实现 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 注册中心:网关要从 Nacos 拉服务列表做负载均衡转发 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 配置中心:路由规则托管在 Nacos,启动时经 bootstrap.yml 引导加载 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.wb</groupId>
<artifactId>mf-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
引导类
package com.mf.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
没有任何特殊注解,网关的能力全部由 starter 自动装配,这也是「网关是配置驱动的接入组件」的直观体现。
配置文件:路由规则在哪
这里有个素材辨证点要交代。讲义(教学进度)给的是本地 application.yml 配路由,端口 8888,规则长这样:
server:
port: 8888
spring:
application:
name: mf-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.136.160:8848
gateway:
routes:
# 手机端访问
- id: mf-app-server
uri: lb://mf-app-server
predicates:
- Path=/app/**
filters:
- StripPrefix=1
# 管理后台
- id: mf-admin
uri: lb://mf-admin
predicates:
- Path=/admin/**
filters:
- StripPrefix=1
四行规则值得逐条拆:
uri: lb://mf-app-server:lb即 load balance,转发目标不是写死的http://localhost:18080,而是服务名,从 Nacos 拉实例列表做负载均衡。对比 SpringCloud 章最初的 cloud-demo,URL 写死的痛点在这里被顺带解决了。predicates: Path=/app/**:断言条件,路径以/app开头才命中这条路由。filters: StripPrefix=1:转发前剥掉第一级路径前缀,/app/user/info到了mf-app-server就变成/user/info,后端服务感知不到网关的存在。- 自定义的
gateway.excludedUrls(/user/login、/user/loginVerification、/system/users/verification、/system/users/login):放行不需要校验 token 的登录类接口,这是鉴权篇的伏笔。
而实读终态工程,bootstrap.yml 里并没有路由配置:
spring:
profiles:
active: dev
application:
name: mf-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.136.160:8848
config:
server-addr: 192.168.136.160:8848
file-extension: yml
因为工程最终把路由规则托管到了 Nacos 配置中心(nacos-config + file-extension: yml,bootstrap.yml 只负责引导连接配置中心)。讲义同步给了 Nacos 侧的同名配置,内容与上面一致。两者不冲突:教学阶段先写在本地 yml 里跑通,工程终态挪去 Nacos 以获得动态刷新能力。这里以讲义的本地版规则为准讲原理,Nacos 托管留到 Nacos 篇延伸。
CorsConfig:跨域配一次
package com.mf.gateway.config;
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 corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source =
new UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}
注意 import 走的是 org.springframework.web.cors.reactive 包,CorsWebFilter 是响应式环境的过滤器,和第七章 MVC 拦截器用的是两套 API。这份配置在网关注册一次,/** 全路径生效,mf-app-server 和 mf-admin 里就不用再配 CORS 了。
一个容易踩的坑:Spring Cloud Gateway 也支持在 yml 里用 globalcors 配跨域,但官网明确提示,globalcors 简单 URL 处理器对部分预检请求可能不生效,用自定义 CorsWebFilter 是更稳的写法。两种写法选其一即可,别同时配。
Spring Cloud Gateway 的角色
一句话:Spring Cloud Gateway 是基于响应式技术栈(Reactor + Netty)构建的 API 网关。它不是普通的 Spring MVC 应用:
| 维度 | Spring MVC 服务(如 mf-app-server) | Spring Cloud Gateway(mf-gateway) |
|---|---|---|
| 底层容器 | Tomcat(Servlet,同步阻塞) | Netty(异步非阻塞) |
| 编程模型 | Spring MVC,@Controller | WebFlux 风格,GlobalFilter 返回 Mono |
| 职责 | 业务逻辑 | 接入层:路由、鉴权、限流、日志 |
对 MVC 应用而言请求是一问一答同步处理的,而网关要扛全量入口流量、大部分时间只做转发,异步非阻塞模型让它用少量线程支撑高并发转发。体现在代码上,就是 AuthFilter 里 filter() 方法的返回类型是 Mono<Void>,而不是传统拦截器的 boolean preHandle()。好在项目的过滤器只做「校验 token、决定放行还是返回 401」,理解成本不高,这是后两篇的事。
统一鉴权预告:TokenInterceptor 怎么办
网关接手 JWT 校验后,各服务里已有的 TokenInterceptor 面临取舍:保留,则网关和服务各验一次(网关挡 401,服务自己也要防御);移除,则服务内彻底信任网关的转发结果。两种口径在生产中都有人用,取舍取决于「内网是否可信」。
工程实读的结论先放在这:mf-gateway 里的 AuthFilter(GlobalFilter + Ordered)已经就位,读取 Authorization 请求头、剥掉 Bearer 前缀、调 JwtUtils.verifyToken 校验,失败返回 401 与 JSON 错误体,excludedUrls 放行登录接口。鉴权思路与代码的完整展开,见本章后续的网关:统一鉴权思路网关:统一鉴权代码实现两篇,那里会正面回答 TokenInterceptor 的去留。
常见误解澄清
| 误解 | 澄清 |
|---|---|
| 微服务就必须有网关 | 网关解决的是接入层问题。单体应用或服务极少、调用方单一的架构,直接暴露服务也未必是问题;服务变多、调用方变杂后网关才划算 |
| 网关可以做业务逻辑 | 网关只承担接入层职责(路由、鉴权、限流、日志),业务逻辑放网关会让它变成新的耦合中心,升级和扩容都被拖累 |
| 有了网关鉴权,服务内部安全不用管 | 网关挡的是外网流量。内网的服务直连端口仍然裸奔,服务内部安全(服务间调用鉴权、内网访问控制)是另一层防护 |
| Gateway 和 Nginx 二选一 | 两者定位不同,通常 Nginx 在最前面做静态资源与反向代理,Gateway 在其后按服务名路由。项目的部署方案正是 Nginx 转发到网关地址 |
| 加了网关,前端要改很多代码 | 前端只需把请求地址从各服务换成网关地址,配合路径前缀 /app、/admin 即可,接口定义本身不变 |
官方文档
总结
- 项目架构的三个接入层痛点(多后端地址、鉴权重复、跨域分散)指向同一个解法:把接入层职责上收到网关。
- 网关两大能力是路由与过滤器;路由解决「进哪个服务」,过滤器解决「进来之前做点什么」。
- 统一鉴权的本质是位置上移:从「每个服务内部的拦截器」变为「所有服务的共同入口」,粒度从单服务变为全项目。
mf-gateway模块极轻:引导类零注解、路由走lb://服务名、终态路由规则托管 Nacos、CORS 用响应式CorsWebFilter配一次。- Gateway 基于 Netty 响应式栈,与 MVC 服务的 Tomcat 同步模型是两套 API,写过滤器时别混用。
- 后面会完善项目架构:搭建网关工程动手创建
mf-gateway模块并跑通路由。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/Tyro_java/article/details/170454384




