2011-11-16 76 views
8

我已經閱讀了大量關於線程安全性和不同版本的ruby和rails的性能的Web上的材料,我認爲在這一點上我很瞭解這些事情。如何部署線程安全的異步Rails應用程序?

討論中似乎很奇怪的是如何實際部署異步Rails應用程序。在談到一個應用程序的線程和同步性,有兩件事情的人要優化:

  1. 利用所有的CPU內核以最小的RAM使用
  2. 能夠滿足新的要求,而以前的要求是在IO
  3. 等待

第1點是人們得到JRuby(正確)興奮的地方。對於這個問題,我只是想優化點2

說這是我的應用程序的唯一控制器:

TheController < ActionController::Base 
    def fast 
    render :text => "hello" 
    end 

    def slow 
    render :text => User.count.to_s 
    end 
end 

fast沒有IO,可以起到每秒請求數百或數千,並slow必須通過網絡發送請求,等待工作完成,然後通過網絡接收答案,因此比fast慢得多。

因此,一個理想的部署將允許數百請求到fast被履行,而slow的請求正在等待IO。

圍繞Web的討論似乎缺少的是堆棧的哪一層負責實現此併發。瘦有一個--threaded標誌,它將「在線程中調用機架應用程序[試驗]」 - 是否爲每個傳入請求啓動一個新線程?在保持並等待傳入​​請求的線程中緩存機架應用程序實例?

瘦是唯一的方式還是有其他人? ruby運行時對於優化點2是否重要?

+0

我懷疑你的ruby版本對於第2點會有多少發言權。這將更依賴於服務器的併發實現以及如何將rails本身放在一起。 – providence

回答

6

您的正確方法很大程度上取決於您的slow方法在做什麼。

在完美的世界中,您可以使用類似於sinatra-synchrony gem的東西來處理光纖中的每個請求。你只能受限於最大數量的光纖。不幸的是,光纖上的堆棧大小爲hardcoded,在Rails應用程序中很容易超載。另外,由於異步IO啓動後的自動屈服,我已經閱讀了一些關於調試光纖困難的恐怖故事。當使用纖維時,競賽條件仍然是可能的。目前,纖維化的Ruby是一個貧民窟,至少在Web應用程序的前端。

不需要更改代碼的更實用的解決方案是使用具有工作線程池(例如Rainbows)的Rack服務器!或美洲獅。我相信Thin的--threaded標誌處理新線程中的每個請求,但旋轉本地OS線程並不便宜。最好使用池大小足夠高的線程池。在Rails中,不要忘記在生產中設置config.threadsafe!

如果您可以更改代碼,可以查看Konstantin Haase的優秀talk on real-time Rack。他討論了使用EventMachine::Deferrable類來產生Rack以外的傳統請求/響應週期以外的響應。這似乎很整潔,但你必須以異步風格重寫代碼。

也看看CrampGoliath。這些可以讓你在一個單獨的Rack應用中實現你的slow方法,這個應用與你的Rails應用一起被託管,但是你可能必須重寫你的代碼才能在Cramp/Goliath處理器中工作。

至於你關於Ruby運行時的問題,它也取決於slow正在做的工作。如果你正在做CPU計算,那麼你就會冒GIL給你帶來問題的風險。如果你在做IO,那麼GIL 不應該是。 (我說不應該,因爲我相信我已經閱讀了關於阻止GIL的老式的MySQL gem的問題。)

就我個人而言,我已經成功地使用sinatra-synchrony作爲後端,混搭web服務。我可以同時向外部Web服務發出多個請求,並等待所有請求返回。同時,前端Rails服務器使用線程池,並直接向後端發出請求。不完美,但現在運行得很好。