Jeremy Evans(Roda 和 Sequel 成名)创建了一个有趣的基准测试比较了各种 Ruby Web 框架中大量路由(高达 10000 个)的内存和运行时性能。我顿时发生兴趣并运行它来获取一些数值。
Jeremy 的基准测试称为 r10k,支持多种 Web 框架。由于结果未包含在原仓库中,因此我在笔记本电脑(第 5 代 i7 ThinkPad Carbon)上运行此基准测试以证明。
我使用最新稳定版本的 Roda(3.16.0)和 Sinatra(2.0.5)以及 Rails 6(6.0.0.beta1)都在 Ruby 2.6.0 下。我只对原始基准测试做了一些小调整,升级 Rails 并移除 config.secret_token
选项。
这儿有一些数字:
app 10 100 1000 10000 <- 路由数
roda 15884 15560 17504 28760
sinatra 23332 24304 29740 100152
rails 41592 43148 52172 115076
如果你对 Roda 比较熟悉那么对它表现最佳的成绩不会感到惊讶,但有趣的是通过使用大量路由,内存占用 Sinatra 比 Rails 更好。如果是 10000 条路由,Rails 只需要比 Sinatra 多 15 MB 的内存,在内存方面 Rails 赢在这里。运行时的性能更有趣:
再来一些数字:
app 10 100 1000 10000
roda 0.172025133855641 0.256997955031693 0.338058794150129 0.446734027005732
sinatra 1.71625644806772 2.67249810113572 11.123352029128 152.814259551931
rails 8.51651063212194 8.59500932297669 9.16402177000418 10.777155773947
Roda 的性能数据对我来说已经不再奇怪了,非常快;Rails 也很不错,确实比较慢,但是在大量路由情况下并没有变得更差,并且其性能是可预测的;不幸的事 Sinatra 在 10000 条路由以上变得很差,并且路由数越大性能越低。
如果我们看看基准测试生成的代码,可以看到它不仅仅是路由数量,还有嵌套层数,每层通过引入目录变成更复杂的路由。这是针对 10000 条路由的:
require 'sinatra/base'
class App < Sinatra::Base
get '/a/a/a/a' do
'4797479747974797'
end
get '/a/a/a/b' do
'4797479747974798'
end
get '/a/a/a/c' do
'4797479747974799'
end
get '/a/a/a/d' do
'47974797479747100'
end
...
end
因此,这并不意味着 Sinatra 不能很好地处理大量路由,但是在大量嵌套路由的情况下的确存在性能问题。我不太熟悉 Sinatra 路由内部原理,所以如果你知道更多(或者如果这个基准测试有 bug)请分享一下。
最后,遗憾的是没有提到很多真是应用中的路由,正如您所看到的,这些路线不是动态的,也不适用于任何参数,因此请使用少量盐(salt)来处理所有路由。