每次部署 Rails 应用,最磨人的不是写代码,而是等它启动。加载代码、读配置、初始化框架,然后才能接受请求。应用越大,这个等待越长,部署、CI(持续集成,代码变更后自动跑测试和构建的流程)、本地开发全被拖住。更麻烦的是,AI agent(能自主执行任务的 AI 程序)现在会频繁启动你的应用——它们每次执行任务都可能拉起一个新进程,启动时间已经从“开发者的耐心”变成了“AI 的算力成本”。
Rails(Ruby 生态最流行的 Web 框架)启动的本质是 Ruby 代码加载过程。一个大型应用要 require(Ruby 中加载一个文件或库的指令)成千上万个文件,gem(Ruby 的第三方库包)之间、应用内部模块之间互相依赖,形成一张复杂的加载图。启动慢,往往不是某一个文件慢,而是 require 的顺序和依赖关系导致重复加载、循环等待、或者某个初始化钩子触发了大量计算。要优化,第一步是定位瓶颈:到底哪些 require 花了最多时间?为什么?
传统做法是用 sampling profiler(采样分析器,定期抓取当前调用栈来统计时间分布)。它在分析运行时性能时很好用,但启动过程是另一回事:代码加载是线性推进的,很多 require 瞬间完成,采样可能根本采不到关键路径。就好比用秒表测接力赛,你只记录每圈总时间,却看不清是哪一棒掉了链子。
require-profiler 就是为这个问题设计的。它专门跟踪 Ruby 的 require 调用,记录每个文件从开始加载到加载完成的时间,以及谁触发了这次加载。拿到这份数据,启动瓶颈一目了然:是某个 gem 初始化太重,还是应用代码里有一个 require 被反复触发?

在 Factorial 的真实项目上,这个工具把开发环境的启动时间砍掉了 40%。那是一个 200 个组件的单体应用(所有功能打包在一个服务里),规模不小,启动慢是长期痛点。用 require-profiler 定位后,优化目标非常明确,不需要靠猜。
更意外的是,在分析一个 AnyCable 驱动的应用时,工具还挖出了 Ruby 自身的一个隐藏行为。AnyCable 是 Rails Action Cable(Rails 内置的 WebSocket 功能)的替代实现,用 Go 处理 WebSocket(浏览器和服务器之间的长连接协议)连接,Ruby 端只负责业务逻辑。这个发现说明:启动性能问题可能藏在框架和语言运行时层面,而不是你的业务代码。
这件事的启发很直接:优化启动时间,应该先用专门的加载追踪工具,而不是一上来就上采样分析器。工具选对了,问题就解决了一半。这个思路也能迁移到其他语言——任何“启动慢”的应用,都值得先问一句:有没有工具能直接追踪模块加载过程?如果有,先跑一遍;如果没有,写一个也不难。
当然,启动优化不是把 require-profiler 跑一遍就完事。它给出的是地图,路还要自己走:减少不必要的 require、用 autoload(Ruby 的延迟加载机制,用到时才加载文件)延迟加载、把重的初始化逻辑挪到真正需要时再执行。但有了地图,至少不会在黑暗里乱撞。
内容与图片版权归原作者所有 · 原文: https://evilmartians.com/chronicles/get-in-human-cut-rails-boot-time-with-require-profiler-and-this-guide