2012-05-09 112 views
9

我在Nginx/Passenger上有一個rails 3應用程序,我剛搬到了Nginx/Thin(1.3.1)。不過,我的應用現在明顯比Passenger慢。很多請求也會超時。瘦服務器表現不佳/服務器Web服務器如何工作?

薄是一個平坦的網絡服務器。從我已經讀過的關於公網服務器的文章來看,他們沒有一個工人的概念。一個「工人」處理所有事情。因此,如果一個請求在IO上等待,thin會繼續執行下一個請求,然後是一個請求。我所讀到的關於服務器平臺的解釋說,服務器應該和基於服務器的服務器一樣好或者更好,因爲它們只受系統資源的約束。

但是,我的CPU使用率很少。我的內存使用量也很少,也沒有太多的IO發生。我的應用程序只是做一些MySQL查詢。

這裏有什麼瓶頸?在CPU達到100%之前,我的瘦服務器不應該處理請求嗎?我是否必須在我的應用程序中執行任何不同的操作,才能使服務器達到更好的性能?

回答

13

塞爾吉奧是正確的。此時,您的應用程序可能更適合傳統的Apache/Passenger模型。如果你採取的是平坦的路線,尤其是在像Ruby這樣的單線程平臺上,那麼無論如何,你都不能阻塞任何東西,無論是數據庫,緩存服務器,還是其他的HTTP請求 - 什麼也不做。

這使得異步編程更加困難 - 通常以同步磁盤I/O或DNS解析的形式阻塞內容很容易。非阻塞(evented)框架(如nodejs)非常小心,因爲它們幾乎不會爲您提供阻塞的框架函數調用,而是使用回調(包括數據庫查詢)處理所有內容。

,如果你看一個單線程非阻塞服務器的心臟,這可能是更容易顯現:

while(wait_on_sockets(/* list<socket> */ &$sockets, /* event */ &$what, $timeout)) { 
    foreach($socketsThatHaveActivity as $fd in $sockets) { 
     if($what == READ) { // There is data availabe to read from this socket 
      $data = readFromSocket($fd); 
      processDataQuicklyWithoutBlocking($data); 
     } 
     elseif ($what == WRITE && $data = dataToWrite($fd)) { // This socket is ready to be written to (if we have any data) 
      writeToSocket($fd, $data);  
     } 
    } 
} 

您在上面看到什麼叫做事件循環。 wait_on_sockets通常由操作系統以系統調用的形式提供,如select,poll,epoll或kqueue。如果processDataQuicklyWithoutBlocking時間過長,OS所維護的應用程序的網絡緩衝區(新請求,傳入數據等)最終會被填滿,並會導致它拒絕新的連接並超時現有的連接,因爲$ socketsThatHaveActivity處理速度不夠快。這與線程服務器(例如,典型的Apache安裝)不同,因爲每個連接都使用單獨的線程/進程提供服務,因此一旦傳入的數據到達應用程序就會讀入應用程序,並且傳出的數據將毫無延遲地發送。

當您創建(例如)數據庫查詢時,像nodejs這樣的非阻塞框架是將DB服務器的套接字連接添加到被監視的套接字列表($ sockets),因此即使您的查詢需要一會兒,你的(唯一的)線程不會被阻塞在那個套接字上。相反,他們提供了一個回調:

$db.query("...sql...", function($result) { ..handle result ..}); 

正如你可以在上面看到,db.query與數據庫服務器上的任何絕對沒有阻塞立即返回。這也意味着你經常要這樣寫代碼,除非編程語言本身支持異步功能(如新的C#):

$db.query("...sql...", function($result) { $httpResponse.write($result); $connection.close(); }); 

的永不不斷塊規則可以是如果你有很多程序有所放鬆每個都運行一個事件循環(通常是運行節點集羣的方式),或者使用線程池來維護事件循環(Java的jetty,netty等,你可以用C/C++編寫自己的代碼)。當一個線程被阻塞時,其他線程仍然可以執行事件循環。但是在負載足夠大的情況下,即使這些也無法執行。所以永遠不要在一臺服務器上堵塞。

正如你所看到的,服務器通常試圖解決一個不同的問題 - 它們可以有很多開放連接。他們擅長的地方就是隻需輕量計算(例如彗星服務器,像memcached,varnish,nginx,squid等代理的緩存)。即使它們擴展性更好,響應時間通常會增加(沒有什麼比爲連接預留整個線程更好),這是毫無價值的。當然,運行與併發連接數相同數量的線程在經濟/計算上可能並不合算。

現在回到你的問題 - 我會建議你保持Nginx的狀態,因爲它在連接管理(基於事件)方面非常出色 - 通常意味着處理HTTP保持活動,SSL等。然後你應該連接這個到使用FastCGI的Rails應用程序,您仍然需要運行工作人員,但不必重寫您的應用程序以完全放完。您還應該讓Nginx提供靜態內容 - 讓您的Rails工作者與Nginx通常可以做得更好的事情聯繫在一起毫無意義。這種方法通常比Apache/Passenger的規模要好得多,特別是如果你運行的是高流量的網站。

如果你可以編寫你的整個應用程序,那就太好了,但我不知道Ruby是多麼容易或困難。

+0

哇..感謝Tejas的詳細迴應。所以我從網上讀取的基準測試...是否適用於完全不同的應用程序類型? Thin自己的網站提供了一個Rails應用程序作爲瘦客戶端示例應用程序。 http://code.macournoyer.com/thin/。我的印象是,我可以用瘦身來代替乘客,一切都會變得h h。 –

+0

只要你不阻止任何地方,你應該能夠重新創建這些基準。 – tejas

3

是的,Thin確實是I/O,但僅限HTTP部分。這意味着它可以在處理請求時接收傳入的HTTP數據。但是,在處理過程中所做的所有阻塞I/O仍然阻塞。如果你的MySQL響應速度慢,那麼精簡請求隊列將會填滿。

對於「更多」的網站服務器,你應該檢查出Rainbows

+0

嗨塞爾吉奧。請原諒我對這些概念的行人理解。我讀到了rails2的mysql2 gem確實也讓IO了。 另外,如果是這樣的話,那麼基於工人的服務器總是不會更好?我只是像2個瘦客戶一樣運行,而不是我之前運行的25個客戶。 –

+0

@CoffeeBite:它可以***做異步調用,是的,但它不是自動的,你必須編寫代碼才能實現。默認情況下,它是同步的。 –

+0

@CoffeeBite:「工人不會總是更好」 - 不確定。我自己在nginx後面使用[Unicorn](http://unicorn.bogomips.org/)。 Nginx處理HTTP I/O,獨角獸可以快速處理請求。 –