击穿 Node.js 的不是性能,而是失控的复杂度:AI 网关的 Go 重构宿命

0 阅读12分钟

最近在看一个 AI API 聚合网关项目

这个项目是一个网关聚合项目,做典型的AI API转发服务,主要的功能就是接受OpenAPI格式的请求,之后转发到不同的Provider,支持Claude/Gemini/OpenAI。可以做Token计费,做SSE流式输出,做账号池和模型路由的功能。

我在思考,这个项目的后端是用Node.js写的,有没有必要用Go将其进行重构。

其实这个问题在一定程度上具有代表性,随着Ai编程的高速发展,很多项目都经历着同样的事情,用python/Node快速开发原型,之后在Ai的辅助下高速迭代,项目的复杂度迅速膨胀,系统逐渐失控,不得不开始考虑用go/Rust将核心模块进行重构优化。

而这个过程,本质上其实不是“语言之争”。或许是在Ai时代,开发的语言已经不是核心,核心是在什么阶段和情况下选择什么技术来做什么事情。


一、AI 出现之后,Node.js 反而重新变强了

在Ai编程日渐变强的今天,很多Ai应用相关项目都喜欢用Node,这是一个很明显的趋势。

我们可以看到 GitHub Octoverse 2025 报告,将其结合 2026 年初的 GitHub Innovation Graph 分析,TypeScript 的活跃度同比激增 66% ,月度贡献者达到 263.6 万,历史上首次超越 Python 和 JavaScript ,成为 GitHub 上使用量第一的语言

目前,有超过 110 万个公共仓库正在使用大语言模型(LLM)的 SDK。截至 2026 年 5 月,专注在 Node.js 生态中构建 AI Agent 和对话式 UI 的核心底层库 Vercel AI SDK,其周下载量已突破 1320 万次

再从 Belitsoft 发布的 2026 年开发状态报告,在需要快速集成 AI 能力的新兴企业级项目中,高达 67% 的项目正在采用基于 Node/TS 的全栈或中间件架构,这一比例自 2023 年以来增长了 300%。

在之前,大家天然觉得,Javza是企业级后端,Go是高性能服务,Python做Ai,Node只是前端写后端。在Ai出现之后,一个很大的变化是“开发速度”被放大到了前所未有的程度,以前

一个人做项目,要自己设计数据库、编写接口到部署上线,面临着巨大的心智负担和开发周期。

现在呢?

Cursor + Claude + ChatGPT:

一天就能给你生成一个完整 MVP,开发速度被放大到了前所未有的决定性地位。

而这时候,Node.js 的优势会被无限放大。

因为它真的太适合:“快速验证产品” 了。

npm 生态几乎什么都有。

AI 对 TypeScript 的理解极强、写起来快、调试简单、改动反馈极快。


最开始的时候,可能是想:“做一个 OpenAI API 转发器”。

但是随着开发的不断进行和项目的不断完善,做着做着就会发现,事情开始变得复杂,我们不仅仅要转发请求,还有处理SSE流式输出Token实时计费、多模型协议转化,限流、风控等等,而项目的复杂度,会在这个阶段突然爆炸。

但是因为后端是用node,并且大量代码由Ai撰写,使得项目的可维护性变差。

Cursor和Claude可以在一秒内仍给你一段精妙的流式解析代码,但它们是“短视”的。

Ai无法像经验丰富的架构师一样,在动笔前就通盘考虑模块间的解耦,状态的原子性和长期扩展的边界,于是,在一轮轮的Prompt迭代中,Ai开始不知疲倦的“往屎山上贴金砖”。

速度是AI给的,但技术债,依然依靠人类程序员深夜掉光头发去偿还。

或许在Ai时代,人类程序员的价值,已经从“写出代码”,彻底升级为“保住代码”。

在这个开发范式转移的节点,我们的核心战线已经开始向后迁移,核心开始变成去保证系统和代码的可维护性,把代码从能跑就行的野生状态,驯化为人类可读,可控的理性秩序。

锚定项目的可发展性,Ai无法预知你未来的项目走向,他无法知道接下来是要从单体架构拆成微服务还是要接入多租户。

只有人类工程师能根据业务的演进蓝图,在架构设计上拓展边界。

人类程序员还要保证工程的可交付性,一个能在本地跑起来的MVP只是玩具,一个通过了严格的风控限流、具备完整的异常处理机制,在线上可以稳定的运行的系统,才叫”工程交付“。

这种对现实环境复杂规则的敬畏与把控,是AI无法理解的。


二、Node.js 其实没那么差,但它有一个致命问题

我们知道,node是基于V8引擎的单线程,只有一个主线程,但其实现了很强的异步I/O,对于那些发网络请求、读写文件、查询数据库这种I/O操作的时候,是通过操作系统底层的多路复用I/O技术、或者隐藏的线程池实现的,这样不会阻塞主进程,在有I/O操作的时候,直接把这个操作扔出去。

既然JS主现场把任务扔出去不管了,那等文件读完了或者网卡收到数据了,JS又怎么知道呢?

这就依靠事件循环机制,再异步任务完成之后,会将这个任务的回调函数塞进一个叫做任务队列的地方,而那个js主现场,会去死循环般的去检查这个任务队列。只要主线程的同步代码执行完了,就会去任务队列中捞出一个回调函数,拿到主线程中执行,执行完再捞下一个,如果后面的代码(B)依赖前面的异步结果(A),那么 B 就必须被强行塞进 A 的回调函数里。

Node 在“默认开发模型”下,更容易获得高并发 I/O 能力。,Java每一个用户HTTP请求过来,Tomcat会从线程池中给这个请求一个现场,如果这个代码要查找MySQL,这个分配出去的线程就会停下来,一直傻傻等待数据库返回结果。如果有1000个并发,就要挂起1000个现场,不仅仅吃内存,还有大量的OS在线程切换的时候浪费的上下文时间。虽然 Java 21 推出了虚拟线程(Virtual Threads)企图收复失控的并发失地,但在轻量化部署和资源压榨上,庞大的 JVM 依然背负着历史的重担。

同样的场景给node,1000个mysql查询请求进来,给底层,然后主线程闲下来,等有返回结果,主线程就在队列中不断拿出来处理,整个过程中没有任何的线程等待。

node为了实现不阻塞,逼迫开发者要用这些异步关键字,用node写后端很痛苦,因为未来满足不阻塞,代价是控制流的支离破碎,心智成本非常高。并且一旦有一段复杂的CPU计算(比如大的JSON解析),那个唯一的JS主现场就会被卡死,整个程序的I/O全部停摆。

这里就要讲讲Go在云原生时代封神的原因了,其完美融合了Java和Node的优点,并消灭了他们的缺点。

Java的代码优点是直上直下,符合人类直觉,缺点是OS线程太重,一等I/O就会卡死。

Node的优点是底层多路复用,完全不阻塞,并发极高,但单线程怕CPU计算,并且异步代码心智负担重。

Go发明了协程和强大的Runtime调度器。在go里面,一个请求就给你一个Goroutine去处理,协程不是操作系统的真实线程,很轻量,只占用2KB,Goroutine 的创建和切换成本远低于 OS 线程,而且代码直上直下,不用await,不写回调。并且底层回通过Runtime调度,把需要等待的协程丛CPU线程上挂起,然后把底层的物理线程分配给其他的协程勇。其底层还是用了类似Node.js的多路复用技术。

如果只有只有网络I/O,或许没有那么强烈的重构必要,但无法忍受的除了开发需要面对的巨大心智负担,那个难用的异步写法,我们本身的业务,在Token的实时增量计费、SSE碎片的拼接和边界处理、JSON的高频动态反序列化等,涉及到大量的CPU密集逻辑,这个时候,这种CPU + I/O的混合负载,会暴露node的后端产生一个可怕后果,一个人的cpu计算,会卡死所有人的i/o。这种cpu计算的时间会在整条链路上堆积,产生大名鼎鼎的Event Loop Blocking(事件循环阻塞)。它的可怕之处在于:一个极端请求引发的 CPU 抖动,会无差别地污染整个 网关 系统的平滑度,产生可怕的系统级长尾延迟。

而 AI Gateway,恰好极其容易踩中这个雷区。 SSE 并不是不断地调 res.write()。在AI 网关里,处理 SSE 是一条极度重工业的流水线:

这条流水线上的每一步(图中红色高亮部分),都充斥着海量的内存 Buffer 拷贝、字符串拼接操作以及剧烈的垃圾回收(GC)压力。 这些操作全都是“CPU 杀手”。它们会不断蚕食、污染原本应该轻盈流转的 Event Loop。

当用 AI 极速堆砌出的 Node.js 原型,一头撞上这种混合负载的高并发场景时,单纯依靠修修补补已经无济于事。


三、为什么很多项目最后还是会走向 Go?

其实我一直觉得,将项目从node重构成go,是因为go比node快,所以我们要用go重构。但是在研究一段时间之后发现,在这几毫秒的性能差异的表层下,真正重构可以解决的事系统逐渐逼近临界点的复杂度失控,这是两个不同纬度的命题。

在项目早期,Node.js像一个极具战斗力的初创团队,凭借极其灵活的动态特性,一个人就可以全栈解决,开发速度十分惊人。但这种“快”,也伴随着一种危险。

AI生成的代码有一个极其致命的短板,它只对“当前这步先跑起来”负责,而对“长期架构的一致性”毫无概念。于是项目开始出现风格不统一、Context泄漏、不敢动核心代码等等问题,系统慢慢变成:

if provider == xxx
else if provider == yyy
else if model == zzz

这种代码组成的巨型状态机,这正是很多AI项目正在经历的事情。

Go是一门极其“无聊”的语言,它没有极其花哨的抽象,没有晦涩的元变成,没有各种各样的动态魔法,用一种近乎独裁的编译器规则,强迫整个项目必须写显示错误处理,必须做明确的类型约束,强制统一的代码风格。

这种克制,限制了程序员的“天马行空的灵感”,却极其有效的控制了抽象的层级,让代码的下限变得极高。系统因此变得不那么容易失控。这个其实才是 Go 最恐怖的地方。

还有一个很现实的问题,AiGateway很多时候都是部署在小服务器上,在这种资源恶劣的情况下,Go对Node实施了纯粹的降维打击。

node.js,在启动的时候要挂载庞大的V8引擎,解析深不见底的node_modules黑洞,还要编译C++ Native Modules。

而Go是静态编译为单一二进制文件,上了服务器,不需要任何环境依赖,瞬间启动,有着极低的内存占用和并发调度。

既然Go这么好,那是不是只要把Node.js代码翻译成Go,一切问题就迎刃而解了?绝对不是。Go不会自动解决架构问题。把一坨用Node.js写的“屎山”,翻译成GO,也只是变成了一坨“强类型的、并发执行的屎山”。

真正的问题可能在:

  • 状态全在内存
  • 单机 Session
  • 定时任务耦合
  • Redis 没拆
  • MQ 没有
  • 水平扩容做不了

这些问题:

换成 Go 一样会存在。

所以真正成熟的重构。

从来不是翻译,而是从单机系统,重构为分布式无状态系统,语言只是其中的一部分。


四、AI 时代,最贵的东西已经变了

最后,我们讨论一个很有意思的问题,在这个被Ai彻底重塑的时代,真正昂贵的东西已经改变了。

在过去的软件工程,最贵的是开发代码,因为代码本身就很难写,写代码本身就是一门高门槛的手艺,每一行逻辑都需要人类用高薪的时间去堆砌。

但当Cursor、Claude、ChatGPT成为常态之后,代码开始以近乎零的边际成本走向全盘贬值,现在,Ai可以在一天那毫无怨言地为你倾泻出成千上万代码。

这使得代码变得越来越便宜,而控制复杂度却变得前所未有的昂贵。

Ai可以给你搭好高楼,但是没有一个Ai可以向你保证,一年之后当面对无数堆叠的布丁的时候,这个系统还是不是还能维护。


所以最后回到最开始的问题。

作为一个 AI API 聚合 网关 ,到底有没有必要从 Node.js 用 Go 重构一遍?

我的答案其实是:

从“性能”角度,未必急。

但从“长期演进”角度,非常值得。

因为真正的问题从来都不是Node.js扛不住,而是系统的复杂度开始失控。

Go 语言最大的价值,本质上就是它用强烈的克制感、显式的错误流和硬性的类型约束,成功驯服了失控的复杂度,它更适合作为一个复杂系统走向长治久安的“长期底座”。