社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

上线一周就重写后端?这家初创团队真的干了:把Python后端全砍掉换成Node

CSDN • 8 月前 • 211 次点击  

产品上线才一周,这个初创团队就做了件大胆的事——他们决定彻底重写后端。而且这次不是继续用炙手可热的 Python,而是换成了 Node。

乍听之下,这像是一场典型的“过早优化”,但他们的理由并不只是性能,而是一次对技术生态、团队节奏与长期可扩展性的全面权衡。

来源:https://blog.yakkomajuri.com/blog/python-to-node

作者 | yakkomajuri      责编 | 苏宓
出品 | CSDN(ID:CSDNnews)

我们刚干了一件疯狂的事:在产品上线仅一周后,我们把后端从 Python 完全重写成了 Node。

为什么要这样做?为了可扩展性没错,就是为了扩展,刚上线一周就这么做了。

从某些角度看,这个时间点其实挺合适的——毕竟当前代码库还小,用户体量也不多。但另一方面,这在很多开发者看来,完全违背了早期创业公司的建议,过往很多人觉得初创公司就是应该把产品先上线、先卖产品,等达到产品市场匹配再考虑扩展。

其实,我们并没有经历过那种用户暴涨、迫使我们快速扩展系统的神奇发布周。通常来说,你选择的技术栈应该能够在很长一段时间内合理扩展,直到你真正需要考虑换框架或用另一种语言重写后端(比如 Rust)的时候。

那么,为什么我们要弃用 Python 而选择 Node?接下来分享我们这么做的具体原因。


Python 的异步真的太糟糕了

我是 Django 的铁粉。在知名的开源产品分析平台 PostHog 项目中,我第一次接触它,从那以后它几乎成了我大部分项目的首选后端框架。Django 能让你快速启动项目,提供优秀的工具和抽象,同时也足够灵活,能按需调整。

所以自然地,当我开始为自己的 Skald(一个用于构建 AI 原生应用的 API 平台,https://github.com/skaldlabs/skald)项目编写后端代码时,也选择了 Django。

问题是,Skald 经常调用大语言模型(LLM)和向量嵌入 API,这意味着我们有大量的网络 I/O,希望是异步的。不仅如此,我们还经常希望并发发送大量请求,比如在为文档的不同片段生成向量嵌入时。

在 Django 中,这些操作很快就变得非常混乱。

我先说明一下,我和同事都没有太多 Python 异步编程经验(我以前主要在 Node 上做异步密集型服务),但我觉得这正是问题的核心:写出稳健高效的 Python 异步代码非常困难,也不直观。你必须深入理解各种底层原理才能做到。

我其实很想花时间真正掌握 Python 异步,但在我们的场景下,你会面临两个问题:

  1. 作为早期创业公司,你会浪费宝贵的时间,而这些时间本该用来上线产品。

  2. 在这个过程中,很容易踩坑,把自己搞得一团糟。

起初,我把问题归咎于自己——脑子里一直在响起“糟糕的程序员!糟糕的程序员!”的声音。虽然更有经验的人会轻松些,但我们发现 Python 异步编程的基础其实也有些不牢固。

与 JavaScript 不同,它从一开始就有事件循环;Go 也创造了 goroutine 的概念(这两种并发模型我都很喜欢,也在生产环境中用过)。而 Python 的异步支持是后来才补上的,这正是困难所在。

有两篇博客很好地解释了这个问题:

  • 《Python has had async for 10 years — why isn’t it more popular?》:https://tonybaloney.github.io/posts/why-isnt-python-async-more-popular.html

  • 《Python concurrency: gevent had it right》:https://harshal.sheth.io/2025/09/12/python-async.html

这两篇内容是在我开始深入研究之前不久发布,非常及时。

从中,我们学到的几点经验:

  • Python 没有原生的异步文件 I/O。

  • Django 仍然没有完全支持异步。ORM 的异步还没做完,colored functions 问题在这里非常明显。理论上可以用 Django 写异步,但官方文档里有太多限制和警告,几乎会吓退任何人。

  • 你到处都得写 sync_to_async 和 async_to_sync。

  • Python 生态中出现了各种模型来改善异步支持,但它们不是原生的,各有局限。例如,aiofiles 提供异步文件操作接口,但底层使用了线程池;Gevent 的 greenlets 很酷,但它实际上是对标准库进行了补丁才能工作。

  • 由于很多 Python 异步支持依赖于语言之上的层,而非原生,你写的异步代码要非常小心,否则会受到运行环境的影响,例如 Gunicorn 的 worker 类型(顺便说一句,从 Gunicorn 文档里很难学到这些)。

总的来说,仅仅想实现一个类似 Promise.all 的功能,同时还要理解它所有的坑,根本不是件简单的事。

面对这个问题,我回到了 PostHog 的代码库看看。

我之前在 PostHog 工作了三年,当时 Django 代码库里完全没有异步,但他们是大公司,现在还加入了 AI 功能,他们肯定已经解决了这些问题!

结果我发现,他们仍然运行的是 WSGI(不是 ASGI),用的是 Gunicorn Gthread workers(最大并发请求通常是 CPU 核心数的 4 倍),所以从异步运行中几乎没有获得多少好处。代码库里有很多工具来让异步正常工作,比如他们自己实现的 async_to_sync。所以我猜,他们处理大量负载的方式可能还是水平扩展

总结一下,Django 没有一个真正优秀的异步解决方案。


那接下来怎么办?

我们基本上得出结论:Django 很快就会成为我们的瓶颈,不只是当负载很大时才会出现问题。

即使用户不多,我们也已经需要开始运行多台机器来避免延迟太高,而且还得写很多笨重、难维护的代码。

当然,我们也可以暂时“做那些不易扩展的事情”,用钱(或者 AWS 额度)解决问题,但感觉不对。而且现在阶段还早,把后端迁移到另一个框架反而会更容易。

这时候,有人可能会建议道:“直接用 FastAPI 吧!”——事实上我们确实考虑过。

FastAPI 支持真正的异步,而且很受欢迎,性能据说不错。如果你想搭配 ORM,可以用 SQLAlchemy,它也支持异步。

迁移到 FastAPI 可能会帮我们省一两天时间(我们的迁移花了 3 天),因为很多代码可以直接复用,无需迁移。但到这个时候,我们对 Python 异步生态整体并不太有信心,而且我们其实已经用 Node 写好了后台 worker 服务,所以我们觉得这是一次机会——干脆全力投入一个生态系统。

于是,我们选择了迁移到 Node。我们花了一点时间挑选框架 + ORM 的组合,最终决定用 Express + MikroORM。

是的,Express 有些老,但经过验证很可靠,而且用起来很熟悉。反正我们迁移的重点就是JS 事件循环


收获与失去

收获:效率

初步基准测试显示,我们的吞吐量提升了大约 3 倍,而且这只是把主要是顺序执行的代码放到异步环境里。现在用 Node 后,我们计划在分块、嵌入、重排序等环节做大量并发处理。这意味着,随着时间推移,这次改动的回报会更大。

失去:Django

失去 Django 让人心痛,而且我们已经发现,在 Express 端需要自己写更多中间件和工具。Node 里也有功能更全面的框架,比如 Adonis,但迁移到一个全新的生态对我们来说工作量太大,所以还是选择了一个最小化的方案。

我最怀念的是 Django 的 ORM,它真的很人性化。虽然在追求极致性能时,ORM 总得小心使用,但 Django ORM 在底层做了很多优化,让你可以在 Python 里写查询而仍有不错性能。我们在把 Django 模型迁移到 MikroORM 实体时,也学到了一些这方面的经验。

收获:MikroORM

MikroORM 是这次迁移中的一个安慰奖。虽然我仍然更喜欢 Django ORM,但不同生态需要不同工具。

以前我从未使用过 MikroORM,但惊喜地发现,它有类似 Django 的懒加载机制,迁移设置感觉比 Prisma 更好,同时 API 也相对人性化(前提是你手动把基础设施搭好)。

总体来说,我们刚开始使用 MikroORM,但目前很高兴在它和原本的 Prisma 之间选择了 MikroORM。

失去:Python 生态

这个其实不用多解释。虽然大多数构建 RAG(Retrieval-Augmented Generation)和智能体的工具都有 Python 和 TypeScript SDK,但 Python 仍然是首选,这里我们只是说 API 封装层。

一旦你想自己深入做 ML,Python 根本无可匹敌。我猜随着我们项目复杂度增加,最终可能还是会有一个 Python 服务,但现在我们还没这个需求。

收获:统一的代码库

我们一直意识到迁移到 Node 意味着我们会有两个 Node 服务,而不是一个 Python 服务加一个 Node 服务。但直到有一天,我们才意识到其实可以把代码库合并,这会非常有帮助。

Node worker 和 Django server 之间有很多重复逻辑,现在我们把 Express server 和后台 worker 统一到了同一个代码库,感觉好太多了。它们都能用同一个 ORM(之前 worker 还得用原生 SQL),还能共享大量工具函数。

收获:更好的测试

这并不是 pytest vs jest 的问题,而是为了确保迁移后所有功能正常,我们写了大量测试。同时做了一些重构,这也是一个很棒的附加收获。


我们是怎么做的?

差不多该总结这篇文章了,但这里简单记录一下实际迁移过程:

  • 我们花了三天完成迁移。

  • 在最后几个环节之前,几乎没用 AI 生成代码——我们觉得理解新架构基础非常重要,特别是新 ORM 的内部运作。基础都搞定后,Claude Code 在生成一些不太重要的接口代码以及扫描代码库问题时帮了大忙。

  • 我们几乎多次想放弃。当时客户不断提出新功能请求,Django 代码里还有一些 bug,让人感觉迁移是在浪费时间,而不是为客户服务。


我们会再做一次吗?

老实说,我们对这个决定非常满意,100% 会再做一次。这不仅在长期会带来回报,实际上现在就已经开始看到好处了。

在这个过程中,我们也学到了很多东西。

如果你想看看实际代码,可以查看以下 PR:

  • skaldlabs/skald#56https://github.com/skaldlabs/skald/pull/56

  • skaldlabs/skald#68https://github.com/skaldlabs/skald/pull/68

Skald 是一个 MIT 授权的 RAG API 平台:https://github.com/skaldlabs/skald

推荐阅读:

开发者生产力“平替”?MiniMax M2全面测评:代码、速度与迁移成本

自研变套壳!开发者逆向200家AI公司前端代码、追踪API:146家实则套壳ChatGPT等,多家技术栈都一样,却赚75倍暴利

苹果刚发布App Store网页版,却意外“泄露”源码?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/188759