既然都能当网关,你凭什么让我在 Reactive 和 MVC 之间“二选一”?
🏆本文收录于《滚雪球学SpringBoot 3》:
https://blog.csdn.net/weixin_43970743/category_12795608.html,专门攻坚指数提升,本年度国内最系统+最专业+最详细(永久更新)。
本专栏致力打造最硬核 SpringBoot3 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot3教程导航帖】https://blog.csdn.net/weixin_43970743/article/details/151115907,你想学习的都被收集在内,快速投入学习!!两不误。
若还想学习更多,可直接前往《滚雪球学SpringBoot(全版本合集)》:https://blog.csdn.net/weixin_43970743/category_11599389.html,涵盖SpringBoot所有版本教学文章。
演示环境说明:
- 开发工具:IDEA 2021.3
- JDK版本: JDK 17(推荐使用 JDK 17 或更高版本,因为 Spring Boot 3.x 系列要求 Java 17,Spring Boot 3.5.4 基于 Spring Framework 6.x 和 Jakarta EE 9,它们都要求至少 JDK 17。)
- Spring Boot版本:3.5.4(于25年7月24日发布)
- Maven版本:3.8.2 (或更高)
- Gradle:(如果使用 Gradle 构建工具的话):推荐使用 Gradle 7.5 或更高版本,确保与 JDK 17 兼容。
- 操作系统:Windows 11
前言:网关这东西,真不是“转发一下就完事儿”😅
我见过不少团队把网关当成“高级版 Nginx”,觉得无非是:
- 把
/api/**转到后端服务 - 加两个 header
- 做个鉴权(最好还是“顺手”)
- 再来点灰度发布(能不能别影响主链路?)
听起来很朴素,但现实是——网关往往是你系统里最容易背锅的那个组件:
- 慢了:说你“拖垮全站”
- 丢 header:说你“把链路搞断”
- 鉴权严了:说你“影响业务转化”
- 鉴权松了:说你“安全事故预备役”
所以我写这篇,不打算用“玄学性能对比”糊弄你,也不搞“把术语堆成一堵墙”。我们就按你的大纲来,掰开揉碎:
- Reactive Gateway vs MVC Gateway(Tomcat / Virtual Threads 语境下怎么选)
- 路由谓词(Predicates)与过滤器(Filters)到底怎么组织、怎么写
- 集成 Token 校验与转发(既要能验,也要能优雅地转发)
- 灰度发布(Canary Release)路由规则怎么落地(不靠玄学)
1. Reactive Gateway vs MVC Gateway:你以为是“编程风格之争”,其实是“运行时模型之争”
先别急着站队。Reactive 不是银弹,Servlet 也不是落伍。这俩的差异,核心在于:请求处理模型、线程模型、扩展点形态不同。
1.1 Reactive Gateway:WebFlux +(通常)Reactor Netty 的那套“事件驱动”
Reactive Gateway 的工作方式,官方文档给的描述非常直白:请求匹配路由后进入 Gateway Web Handler,然后跑一条“前置/后置”过滤链,前置跑完再发代理请求,回来再跑后置过滤逻辑。
这意味着什么?
- 你通常在 Reactor/Netty 的运行时语境里思考问题
- 你写 filter 时经常要面对 Mono/Flux(优雅也好、劝退也罢)
- 高并发 I/O 场景下,吞吐与资源占用往往很能打(尤其是网关这种“以 I/O 为主”的角色)
1.2 MVC Gateway:WebMvc.fn + Servlet 容器(Tomcat/Jetty/Undertow)那套“我熟啊”
MVC Gateway 的官方定位也很清晰:它的路由本质上就是 WebMvc.fn 的 RouterFunction,用一个“特殊的 HandlerFunction”去做 HTTP 转发,同时提供额外的 RequestPredicate 与 HandlerFilterFunctions 供你拼装。
翻译成人话:
- 你仍然活在 Servlet 容器里(Tomcat 等)
- 路由匹配走 WebMvc.fn HandlerMapping
- 过滤链是 HandlerFilterFunction/before/after 这种“函数式但不反人类”的形态
小吐槽:WebMvc.fn 这套 API 很像“Spring 给你一块乐高板”,你喜欢拼就拼;但如果你团队里有人只写注解 Controller,看见 RouterFunction 可能会皱眉。别慌,后面我会把写法讲得很落地。
1.3 “Tomcat + Virtual Threads”为什么会让 MVC Gateway突然变得更诱人?
以前很多人选 Reactive,是因为 Servlet 阻塞模型在高并发下容易“线程顶不住”。但 Java 21 的虚拟线程(Virtual Threads)带来一个很现实的变化:阻塞不再必然等于“昂贵的 OS 线程阻塞”。
- OpenJDK 的 JEP 444 对虚拟线程的定位很明确:它是轻量线程,目标是显著降低高吞吐并发应用的开发与维护成本。
- Spring Boot 文档也明确提到:在 Java 21+ 且
spring.threads.virtual.enabled=true时,Boot 会自动用支持虚拟线程的执行器(例如 SimpleAsyncTaskExecutor 使用虚拟线程)。
注意:这不等于“Tomcat 立刻变成 Netty”,也不等于“阻塞就没有代价”。但它确实让你可以用更接近传统同步代码的方式,去承接更高的并发密度,尤其在 I/O 密集时更明显。
1.4 选型别靠情绪:我给你一张“冷静点”的对照表
| 维度 | Reactive Gateway | MVC Gateway |
|---|---|---|
| 运行时 | WebFlux +(常见)Reactor Netty | WebMvc.fn + Servlet 容器(Tomcat 等) |
| 编程心智 | Mono/Flux、反压、链式操作 | RouterFunction / RequestPredicate / HandlerFilterFunction |
| 适用场景倾向 | 极致 I/O 并发、全链路响应式、已经 WebFlux 化 | 团队偏 Servlet、要复用既有 MVC 技能栈、想结合虚拟线程 |
| 扩展点体验 | GlobalFilter/GatewayFilter(响应式) | before/after/filter(函数式,偏同步) |
| “落地阻力” | 响应式心智门槛可能更高 | 对传统 Spring MVC 团队更友好(但要接受 fn 风格) |
我的建议(中立口吻,不劝架):
- 你们全栈都在 WebFlux、并且对 Reactor 很熟:Reactive Gateway继续用,别折腾。
- 你们大量是 Servlet/MVC、希望网关开发更贴近“同步直觉”,并且准备上 Java 21:MVC Gateway 很值得试。
2. 路由谓词(Predicates)与过滤器(Filters):网关的“骨架”和“肌肉”分别是什么?
先把概念捋顺,不然后面写灰度、写鉴权,你会越写越像在掷骰子🎲。
2.1 在 MVC Gateway 里:Route = RouterFunction + Predicate + Filter
官方描述很关键:MVC Gateway 的 routes 是普通 WebMvc.fn RouterFunction,并且提供额外的 RequestPredicate 与 HandlerFilterFunctions。
所以你可以把它理解成:
- Predicate:决定“这请求归不归我管”
- Filter:决定“我管了之后要做什么手脚”(改请求、加头、鉴权、限流、改响应……)
2.2 先看 Predicate:匹配规则不是越多越好,是越“可解释”越好
Spring Cloud Gateway MVC 的谓词匹配是 WebMvc.fn 的 HandlerMapping 体系里做的,并且可以组合 and/or。
2.2.1 YAML 方式(更像“声明式路由表”)
spring:
cloud:
gateway:
mvc:
routes:
- id: user_api
uri: http://user-service:8080
predicates:
- Path=/api/users/**
- Method=GET
- Header=X-Request-Id, \d+
filters:
- AddRequestHeader=X-Gateway, mvc
- StripPrefix=1
上面这个配置里:
predicates决定路由命中:Path、Method、Header 这些都属于官方文档列出的 MVC Predicate(比如 Header 谓词示例里用正则匹配 header 值)。filters则做转发前的加工(AddRequestHeader、StripPrefix 等属于 MVC FilterFunctions 的文档范围,具体有哪些你可以在“Gateway Handler Filter Functions”章节里查到入口)。
2.2.2 Java Routes API(更适合“规则需要编程表达”的情况)
官方在 Predicate 文档里就给了 Java 写法示例,比如 between(...)、header(...) 这类静态方法来自 GatewayRequestPredicates。
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.header;
@Bean
RouterFunction<?> routes() {
return route("user_api")
.route(path("/api/users/**").and(header("X-Request-Id", "\\d+")), http())
.before(uri("http://user-service:8080"))
.build();
}
你看,读起来就像:
如果 path + header 命中,就把请求交给 http() 这个 handler,然后 before(uri(…)) 指定下游地址。
这就是 MVC Gateway 的“拼乐高”气质:清晰,且可组合。
3. Filters:别把过滤器当“万能钩子”,它应该有边界感
MVC Gateway 的自定义扩展点,官方说得很实在:
- 你可以写自定义
RequestPredicate - 也可以写
HandlerFilterFunction - 还可以写更轻量的 before/after(本质上也是 filter 的特化)
3.1 自定义 Predicate:用来表达“业务语义的匹配条件”
官方示例展示了如何写一个检查 header 是否存在的 predicate:本质就是 boolean test(ServerRequest request) 的函数式实现。
我们稍微“升级”一下:写一个 命中灰度用户的 predicate(后面灰度发布要用)。
思路:
从请求里拿一个“稳定标识”(比如 X-User-Id 或 cookie),做一个一致性 hash,然后按百分比命中。
import org.springframework.web.servlet.function.RequestPredicate;
import org.springframework.web.servlet.function.ServerRequest;
import java.nio.charset.StandardCharsets;
import java.util.zip.CRC32;
public class CanaryPredicates {
// percent: 0~100
public static RequestPredicate canaryByUserIdHeader(String headerName, int percent) {
int p = Math.max(0, Math.min(100, percent));
return request -> {
String uid = request.headers().firstHeader(headerName);
if (uid == null || uid.isBlank()) return false;
long bucket = stableBucket(uid);
return (bucket % 100) < p;
};
}
private static long stableBucket(String input) {
CRC32 crc = new CRC32();
crc.update(input.getBytes(StandardCharsets.UTF_8));
return crc.getValue();
}
}
这种写法的好处是:
- 稳定:同一个用户不会今天进灰度、明天又被踢出来(除非 percent 变了)
- 可解释:你能清楚解释“为什么这个用户会命中”
- 可控:改个 percent 就能调流量
而且它完全符合 MVC Gateway 推荐的扩展姿势:自定义 RequestPredicate。
3.2 自定义 Filter:别在这里写“业务逻辑”,写“网关职责”
官方示例演示了如何写一个同时给请求与响应加 header 的 HandlerFilterFunction:先改 request,再 next.handle(modified),最后改 response。
那我们来写一个更网关一点的:Token 校验(JWT)+ 透传用户信息。
4. 集成 Token 校验与转发:既要“能验”,也要“能转发”,还要“不恶心人”
这里我分两类讲,因为很多团队会混用:
- 网关自己验 JWT(网关像 Resource Server)
- 网关做 TokenRelay(OAuth2 登录后的 access token 转发给下游)
4.1 网关验 JWT:用 Spring Security Resource Server 的“官方姿势”
Spring Security 官方文档明确:JWT 资源服务器支持主要在 spring-security-oauth2-resource-server,而 JWT 解码验签在 spring-security-oauth2-jose,两者都需要。
这段话非常关键,因为它意味着:别只引 resource-server 还指望 JWT 自动验签,缺 jose 你会卡得很尴尬。
同时,Bearer Token 默认从 Authorization header 里取(当然也能自定义)。
4.1.1 依赖(示例)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
(JWT 验签相关的 jose 依赖会随 starter 引入,具体以你 BOM 为准;概念上资源服务器与 jose 都是必要构成。
4.1.2 配置:让网关按 issuer-uri/jwk-set-uri 自动验签(概念示例)
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.com/issuer
这类配置方式是 Spring Security 官方“资源服务器 JWT”章节的主流路径(通过 issuer 发现公钥与元数据)。
4.1.3 安全链(Servlet)示例:放行健康检查,其余都要 token
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2.jwt())
.build();
}
这条链的意义很简单:网关先把“有没有合法 token”这件事扛下来,后面你在 Gateway filter 里就不用重复造轮子了(否则你会写出两个不一致的鉴权实现,最后自己都怕)。
4.2 Token 转发:用 MVC Gateway 的 TokenRelay(官方内置,不用硬抄 header)
很多团队最爱“手动转发 Authorization: Bearer …”,但当你接入 OAuth2 Client(比如用户登录)时,正确姿势通常是:让网关从已认证上下文中取 token,然后转发给下游。
Spring Cloud Gateway MVC 官方文档明确提供 TokenRelay filter:
- 它能把 OAuth2 access token 转发给下游服务
- 支持可选参数
clientRegistrationId - 依赖需要
spring-boot-starter-oauth2-client
4.2.1 Java Routes API 用 TokenRelay(官方示例风格)
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.filter.TokenRelayFilterFunctions.tokenRelay;
@Bean
RouterFunction<?> tokenRelayRoute() {
return route("resource")
.GET("/resource", http())
.before(uri("http://localhost:9000"))
.filter(tokenRelay()) // 不传 registrationId:转发当前已认证用户的 token
.build();
}
这就是“官方给你的现成轮子”:别硬抄 header,让框架按 OAuth2AuthorizedClientManager 的机制去拿 token,更稳。
4.2.2 YAML 方式
spring:
cloud:
gateway:
mvc:
routes:
- id: resource
uri: http://localhost:9000
predicates:
- Path=/resource
filters:
- TokenRelay=
同样是官方文档给出的配置方式。
5. “校验 + 转发”怎么组合得更舒服?给你一个实战套路
我个人更喜欢一个“边界清晰”的组合:
- Spring Security Resource Server:负责“token 合法性”
- Gateway Filter:负责“把必要的用户信息打到下游”(例如 userId、tenantId、traceId)
- TokenRelay(可选):如果你是 OAuth2 Client 场景,就用它转发 access token
5.1 一个“网关该干的事”Filter:注入用户信息 + 统一 Trace
下面这个例子会做三件事:
- 读取已认证用户信息(从 SecurityContext / Principal)
- 生成/透传
X-Request-Id - 把
X-User-Name或X-User-Id这类 header 塞给下游
这里我用 WebMvc.fn 的
HandlerFilterFunction写法,符合官方的自定义 filter 方式:filter(ServerRequest, HandlerFunction)。
import org.springframework.web.servlet.function.HandlerFilterFunction;
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.servlet.function.ServerResponse;
import java.util.UUID;
public class GatewayFilters {
public static HandlerFilterFunction<ServerResponse, ServerResponse> enrichIdentity() {
return (request, next) -> {
String requestId = request.headers().firstHeader("X-Request-Id");
if (requestId == null || requestId.isBlank()) {
requestId = UUID.randomUUID().toString();
}
// Principal 可能来自 Spring Security 已认证上下文
String user = request.principal().map(Object::toString).orElse("anonymous");
ServerRequest modified = ServerRequest.from(request)
.header("X-Request-Id", requestId)
.header("X-User", user)
.build();
ServerResponse response = next.handle(modified);
response.headers().add("X-Request-Id", requestId);
return response;
};
}
}
这段代码的“气质”就很网关:
- 不做业务判断(比如“用户能不能下单”——那是业务服务的事)
- 只做跨服务一致性的增强(identity、trace、headers)
6. 灰度发布(Canary Release):别把灰度当“随机抽奖”,它应该可控、可回滚、可解释
灰度发布在网关层做,常见诉求就三类:
- 按人灰度(某些用户/租户进新版本)
- 按比例灰度(5% 流量进新版本)
- 按特征灰度(特定 header、特定地区、特定渠道)
MVC Gateway 在 Predicate 层就很适合做这件事:你可以组合内置谓词(Header、Cookie、Query、Path 等),也可以像官方建议那样自定义 RequestPredicate。
6.1 最“稳妥”的灰度:显式标记(Header/Cookie/Query)
这种方式最适合:
- 内部测试
- 小流量验证
- 需要快速回滚(删 header 就完事)
例子:带 X-Canary: true 的请求去 v2,否则去 v1
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.header;
@Bean
RouterFunction<?> canaryByHeaderRoutes() {
return route("order_v2_canary")
.route(path("/api/orders/**").and(header("X-Canary", "true")), http())
.before(uri("http://order-service-v2:8080"))
.build()
.and(route("order_v1_default")
.route(path("/api/orders/**"), http())
.before(uri("http://order-service-v1:8080"))
.build());
}
这里 header(...) 的用法来自官方 Predicate 文档示例(Header predicate 支持正则匹配)。
6.2 按用户稳定灰度:一致性哈希 + 百分比(推荐长期使用)
这就是前面我们写的 CanaryPredicates.canaryByUserIdHeader(...) 的用武之地。
目标:同一个用户稳定命中同一个版本,且只要调 percent 就能放量/收量。
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path;
import static CanaryPredicates.canaryByUserIdHeader;
@Bean
RouterFunction<?> canaryByUserStableRoutes() {
int percent = 5; // 先灰 5%,别上来就 50%,别冲动😅
return route("pay_v2_canary")
.route(path("/api/pay/**").and(canaryByUserIdHeader("X-User-Id", percent)), http())
.before(uri("http://pay-service-v2:8080"))
.build()
.and(route("pay_v1_default")
.route(path("/api/pay/**"), http())
.before(uri("http://pay-service-v1:8080"))
.build());
}
这种方式的“工程优势”很明显:
- 可回滚:percent 设回 0,瞬间回全量 v1
- 可解释:用户 A 为什么进 v2?因为 hash bucket 命中
- 可审计:你甚至可以把 bucket 写到 header 里,排查问题特别省心
6.3 “按比例随机灰度”为什么我不太爱?
也不是不能用,而是它经常会带来两类麻烦:
- 用户体验不稳定:同一个用户第一次命中 v2,下一次又跑回 v1,客服会被你逼疯
- 问题定位困难:线上 bug 复现像抽卡,抽不到就只能靠猜
除非你灰度的是纯静态、无状态、幂等行为,否则“按用户稳定灰度”通常更友好。
7. 把前面拼起来:一个“能鉴权、能转发、能灰度”的 MVC Gateway 样例骨架
我给你一个相对完整的骨架(省得你把零件散落一地):
- Spring Security Resource Server:验证 JWT(合法性)
- Gateway filter:注入 trace + user 信息
- Canary predicate:5% 用户到 v2
- 其他用户到 v1
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;
import static org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;
import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;
import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.uri;
import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.path;
import static CanaryPredicates.canaryByUserIdHeader;
@Configuration
public class GatewayRoutesConfig {
@Bean
RouterFunction<ServerResponse> gatewayRoutes() {
int canaryPercent = 5;
RouterFunction<ServerResponse> v2 = route("order_v2_canary")
.route(path("/api/orders/**").and(canaryByUserIdHeader("X-User-Id", canaryPercent)), http())
.before(uri("http://order-service-v2:8080"))
.filter(GatewayFilters.enrichIdentity())
.build();
RouterFunction<ServerResponse> v1 = route("order_v1_default")
.route(path("/api/orders/**"), http())
.before(uri("http://order-service-v1:8080"))
.filter(GatewayFilters.enrichIdentity())
.build();
return v2.and(v1);
}
}
你会发现它读起来非常“直觉化”:
- 先写清楚路由规则(predicate)
- 再写清楚转发目标(before(uri))
- 再挂上网关职责 filter(enrichIdentity)
- 最后把两条路由拼在一起(and)
这就是 MVC Gateway 的一个明显优点:“规则像规则,代码像代码”。
8. 小心坑:别等线上炸了才想起这些细节(我是真见过…😵💫)
8.1 “网关验 JWT”与“下游再验 JWT”会不会重复?
会。但很多时候这是合理的重复:
- 网关验:挡掉明显非法请求,保护下游
- 下游验:防止绕过网关(内网误配、旁路调用、测试环境“临时开口子”)
你可以把网关验当作“第一道门禁”,下游验当作“重要区域的二次门禁”。安全这事,别太相信“不会有人绕路”。
8.2 TokenRelay 不是“万能转发”,它是 OAuth2 Client 语境下的转发
TokenRelay 的定义是:OAuth2 consumer 作为 Client,把传入 token 转发给下游资源请求。并且它依赖 OAuth2 Client 配置与 OAuth2AuthorizedClientManager 的生成。
所以如果你是“纯 JWT 无登录态”的模式,TokenRelay 不一定是你的主角;你更可能做的是:
- 网关校验 JWT(resource server)
- 然后把原始 Authorization 透传(或把用户信息展开为 header)
8.3 Virtual Threads 很香,但别把它当“性能许愿池”
虚拟线程是 Java 平台层面的能力(JEP 444),目标是降低并发编程成本。
Spring Boot 也提供了开关来启用虚拟线程相关执行器。
但你仍然需要关注:
- 阻塞点是不是“可卸载”的(例如某些同步锁、ThreadLocal 滥用会影响伸缩性)
- 你的下游依赖(DB/HTTP 客户端)是否会把收益吃掉
- 监控/排障工具链是否适配(线程数量暴涨时,某些指标的解读方式要调整)
9. 结语:你真的需要“非黑即白”的选型吗?😌
说句有点“人间真实”的话:
很多选型争论,吵到最后不是技术输赢,而是团队心智与交付节奏的输赢。
- Reactive Gateway 很强,尤其当你已经全链路响应式,或者你需要极致 I/O 并发吞吐。
- MVC Gateway 很“顺手”,它建立在 WebMvc.fn 的 RouterFunction / Predicate / Filter 模型之上,让很多传统 Spring 团队能更低摩擦地写出清晰的网关规则。
- 虚拟线程让“同步直觉 + 高并发”这件事变得更现实一些,但它不是魔法。
最后送你一句我自己写网关时经常默念的“防背锅口诀”:
路由要可解释,鉴权要有边界,灰度要可回滚,转发要可观测。
否则你总会在某个周五晚上收到一句:“你们网关是不是又改了啥?”🙂
🧧福利赠与你🧧
无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学SpringBoot」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门SpringBoot,就像滚雪球一样,越滚越大, 无边无际,指数级提升。
最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。
同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G PDF编程电子书、简历模板、技术文章Markdown文档等海量资料。
ps:本文涉及所有源代码,均已上传至Gitee:
https://gitee.com/bugjun01/SpringBoot-demo开源,供同学们一对一参考 Gitee传送门https://gitee.com/bugjun01/SpringBoot-demo,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗
🫵 Who am I?
我是 bug菌:
- 热活跃于 CSDN:
https://blog.csdn.net/weixin_43970743| 掘金:https://juejin.cn/user/695333581765240| InfoQ:https://www.infoq.cn/profile/4F581734D60B28/publish| 51CTO:https://blog.51cto.com/u_15700751| 华为云:https://bbs.huaweicloud.com/community/usersnew/id_1582617489455371| 阿里云:https://developer.aliyun.com/profile/uolxikq5k3gke| 腾讯云:https://cloud.tencent.com/developer/user/10216480/articles等技术社区; - CSDN 博客之星 Top30、华为云多年度十佳博主&卓越贡献奖、掘金多年度人气作者 Top40;
- 掘金、InfoQ、51CTO 等平台签约及优质作者;
- 全网粉丝累计 30w+。
更多高质量技术内容及成长资料,可查看这个合集入口 👉 点击查看:https://bbs.csdn.net/topics/612438251 👈️
硬核技术公众号 「猿圈奇妙屋」https://bbs.csdn.net/topics/612438251 期待你的加入,一起进阶、一起打怪升级。
- End -
- 点赞
- 收藏
- 关注作者
评论(0)