跳至主要內容

十万个Why:内网服务明明可以直连,为什么一定要加一层 API 网关?

程序员小富大约 5 分钟

大家好,我是小富~

《十万个why》系列持续更新~

既然服务都是内网环境,都在一个物理机房或者 VPC 里,我直接通过 Consul 或者 K8sService 发现对方,直接调 IP 不就完了吗?为什么非得加个 API 网关转一手?这不明显增加延迟、还多了一个单点故障风险吗?

这个问题我刚入行时也不理解。从网络拓扑上看,直连是最快的。但互联网业务架构太复杂,快往往不是唯一的追求,可控才是。

解决混乱的服务治理

假设你手头有 50 个微服务。如果采用直连,那么限流、熔断、日志追踪、灰度发布的逻辑,打算写在哪里?如果写在每个服务的代码里,那这简直是维护噩梦!

举几个例子:

版本不一致: A 服务用的限流组件是 V1.0,B 服务更新到了 V2.0,配置参数都不一样。

语言壁垒: 核心链路用 Java,高性能模块用 Go,算法模块用 Python。总不能给每种语言都撸一套功能完全对等的治理 SDK?

网关的本质是横切关注点的剥离。

把这些跟业务无关、但跟架构有关的逻辑抽离出来。这样无论后端是 Java 还是 Go,进入内网的流量,必须在网关层统一被限流、被标记(TraceID)、被监控。

灰度发布

生产环境最怕的是全量上线,大厂一般要做金丝雀发布或者灰度测试,比如让 1% 的流量走新版 B 服务,剩下 99% 走旧版。

如果是服务间直连,需要在调用方去写复杂的负载均衡策略,去判断哪些请求该发往哪个 IP,这把业务逻辑和部署逻辑深度耦合了。

有了网关,这个事儿就变得非常优雅:

网关通过配置中心动态下发路由规则,可以进行流量调度,根据 Header 里的版本号、UserID 甚至是自定义标识,丝滑地把流量拨到不同的集群。业务代码完全感知不到这个切换过程。

协议适配

而且实际上不同部门项目的编程语言繁杂,用啥提供服务能力的都有!

有的老服务还在跑 SOAP 或者老的 RESTful,新服务已经全面转向了 gRPC 或者 Dubbo。如果让它们直连,你会发现各个服务之间为了互通,光是引入依赖包、搞定协议转换就能让开发者崩溃。

网关在这里充当了翻译的角色。

可以对外暴露统一的标准 RESTful 接口,对内根据不同的路由,自动转换成对应的协议,比如 HTTP 转 gRPC。这样调用方不需要关心被调用方到底是用什么语言、什么协议写的,只管按规矩发请求就行。

安全与解耦

内网是绝对安全的其实是一个典型的伪命题。

如果没有网关,所有的微服务接口都相当于直接暴露在内部网络中。如果某个服务的端口泄露,或者内部权限管控不严,任何一个内网节点都能尝试扫描并攻击你的核心服务。

网关隐藏了后端服务的具体 IP 和端口,对外只暴露一个虚拟的 URL。更重要的是,它实现了服务接口的收敛

有些内部接口,比如清空缓存、导出全量数据的 Admin 接口,我们可以直接在网关层配置策略,禁止从非特定区域发来的请求通过,不需要在每个业务代码里加逻辑判断。

高并发流量的缓冲

直连模式下,如果 A 服务突然爆发式请求 B 服务,B 服务往往只能硬顶,顶不住就宕机。

虽然我们可以在 B 服务本地做限流,但请求已经过来了,TCP 连接已经建立了,系统资源已经消耗了。

而在网关层,我们可以做全局限流,在流量还没真正触达具体的业务机器之前,就把那些超限的、异常的请求直接拦截了。这种防线前移的操作,在应对 618 或者大促活动时,是保住系统可用性的关键。

当然了,网关实际的使用场景要比这些多得多!

回答疑惑

回到前边说过的质疑:网关增加了延迟吗?

增加了!!!即便再高性能的网关,也会有几毫秒的转发损耗。

网关是单点故障吗?

是。

所以我们要搞高可用集群,搞多副本部署。但现在的微服务架构,这点性能损耗相比于它带来的统一治理、无感灰度、协议解耦和安全收敛,性价比实在是太高了。

说在最后

我的建议是,如果是只有三五个服务的小项目,直连确实更香,别为了网关而网关了,没必要搞!一旦服务数量过十,且有跨团队协作、频繁迭代的需求,网关就很有必要了

做技术没有绝对的对错,只有在特定场景下的取舍。网关的存在,就是用一点点物理上的延迟,换取逻辑上极大的自由度。

上次编辑于: