Rails3: 新的 Metal 機制

新的 Rails3 Controller 重構後,變成 ActionController::Base < ActionController::Metal < AbstractController 的繼承體系。除了 ActionMailer 繼承自 AbstractController,讓 API 與 Controller 一致之外,新的 ActionController::Metal 更是充滿玩味。

故事可以從上個禮拜,Yehuda 把 Rails 2 的 Metal 移除了(commit),根據 commit 的說明,Rails 2 的 Metal 在 Rails 3 裡面,可以用 1. 放在 Route 之前的 Rack middleware 或 2. 放在 Route 之後的一個 Rack endpoint,而這個 Rack endpoint,除了自己實作 Rack app,我們也可以用 ActionController::Metal 來實作出自己的客製化 Controller。

Rails2 發明 Metal 原因是,有些事情不需要完整的 Controller 功能,希望能夠越快越好,例如 XML/JSON API 等。而 Rails2 的 Metal 雖然非常快,但是沒什麼彈性,完全不能使用 Controller 的功能,例如用 Layout, Template, Sessions 等,基本上就跟單純的 Rack middleware 沒什麼兩樣。但是在 Rails3 中,可以透過自繼承 ActionController::Metal 做出白紙般的客製 Controller,可以有非常快的速度,如果有需要用到 Controller 的功能,也可以任意選擇組合加入,十分彈性。

例如,我們來實作一個超級精簡的 Static Controller:


# lib/static_controller.rb
class StaticController < ActionController::Metal
  include ActionController::Rendering
    
  append_view_path "#{Rails.root}/app/views"
    
  def about
    render "about"
  end
end
    
# config/route.rb
match '/about', :to => "static#about", :as => :about

這個範例有接近於 Metal 的速度,並加入了 Controller 的 Template 功能 (你可以參考 ActionController::Base 這是擁有全部功能的版本)。其中 “static#about” 是 StaticController 的 about action 縮寫,而在 Rails3 中,controller action 也都是一個 Rack app。

除了我的這個簡單範例,在 The Powerful New Rails RouterUpgrading a Rails 2 App to Rails 3 的影片中,也有分別舉例。

總而言之,如果你在 Rails3 中不需要全部的 Controller 的功能,想要盡量拉高效能,有幾種推薦作法:

* 寫成 Rack Middleware,然後在 config/application.rb 中插入 config.middleware.use YourMiddleWare
* 寫成 Rack App,在 config.route.rb 中將某個對應的網址指到這個 Rack App
* 繼承自 ActionController::Metal 實作一個 Controller,其中的 action 也是一個 Rack App

其中的差異就在於後兩者會在 Rails Route 之後(好處是統一由 route.rb 管理 URL 路徑),如果繼承自 ActionController::Metal 可以有彈性獲得更多 Controller 功能。原則上,我想我會推薦 ActionController::Metal,寫起來最為簡單,一致性跟維護性較高。

另外,還有個小玩意, ActionController::Middleware 是 Controller 層級的 Rack Middleware,讓你可以在放入到某個特定 Controller 之中(也就是只有該 Controller 使用這個 Middleware)。不過呢,這個功能我到現在還沒看到任何實用的例子就是了。

最後,Yehuda 提供了一個 參考數據


fast: through middleware inserted at 0
slwr: through middleware inserted via @use@
rotr: through endpoint sent via the router
bare: through ActionController::Metal with self.response_body
cntr: through ActionController::Base with self.response_body
text: through ActionController::Base with render :text
tmpl: through ActionController::Base with simple render :template
layt: through ActionController::Base with render :template with layout

         real     rps
fast   0.004271   2900 Rack 極限
slwr   0.067029   2200 使用 config.middleware.use YourMiddleware
rotr   0.088085   2000 經過 Rails Route 之後
bare   0.103868   1900 使用 ActionController::Metal 的最快速度
cntr   0.355898   1070 使用 ActionController::Base 的最快速度
text   0.557127    825 使用 ActionController::Base 加上 render :text
tmpl   0.639581    765 使用 ActionController::Base 加上 render :template
layt   1.678789    375 使用 ActionController::Base 加上 Template 跟 Layout

參與討論

1 則留言

發佈留言

發表迴響