Skalowanie RoR

Opublikowane przez Jarosław Zabiełło Sun, 06 Aug 2006 12:54:00 GMT

Mit o rzekomej niskiej wydajności Railsów wydaje się być przesadzony. Interpreter Rubiego jest co prawda wolniejszy od interpretera Pythona (i tym samym Django jest szybsze od Railsów). Ale już porównanie Django, Railsów i pehapowego Symphony pokazuje że Rails jest wyraźnie szybszy od Symphony (który używa PHP5 i akceleratora!). Skoro PHP jest wolniejszy, to z szybkością Railsów nie jest aż tak źle, prawda? Ale to nie wszystko, Rails posiada wygodny mechanizm który pozwala bez trudu powalić na kolana typową aplikację Django.

Siła skalowania poziomego.

Gdy mamy na myśli wydajność całej aplikacji to nie sposób pominąć ważny jej element: skalowalność. Aplikację webową można skalować pionowo (dokładając więcej RAM’u, mocniejszy procesor) lub poziomo (dokładając po prostu kolejny serwer). Ta ostatnia opcja jest szczególnie interesująca, bo dołożenie mocniejszego procesora nie można ciągnąć bez końca.

Ruby on Rails świetnie skaluje się poziomo. Zamiast klasycznego zestawu Lighttpd + kilka procesów fasctgi polecam znacznie lepszy zestaw oparty na procesach mongrel.

Scenariusz 1:

Lighttpd na froncie obsługuje wszystkie statyczne pliki. Pozostałe kieruje do Pound (szybkiego i lekkiego load balancera1), który z kolei kieruje ruch do kilku procesów mongrela (każdy z nich może być nawet na innej maszynie).

Scenariusz 2:

Apache 2.2 na froncie z wbudowanym modułem load balancera rozkłada ruch na kilka procesów mongrela.

Przykład.

Zakładam, że użyję Lighttpd + Pound + 4 procesy mongrela z rozłożonym ruchem na 2 serwery. Fragment pliku konfiguracyjnego dla serwera Lighttpd:

$HTTP["host"] == "domena.host" {
  $HTTP["url"] !~ "^/(cgi-bin|images|stylesheets|javascripts)" {
    proxy.server = ( "/" => ( ( "host" => "127.0.0.1" , "port" => 9000 ) ))
  }
  server.document-root = "/home/httpd/domena.host/public"
  server.indexfiles = ("dispatch.fcgi")
  server.error-handler-404 = "/dispatch.fcgi"
}

Lighttpd obsługuje wszystkie pliki statyczne. Robi to wyjątkowo szybko, lepiej niż Apache. Cały pozostały ruch jest spięty za pomocą proxy z procesem na porcie 9000. Na tym porcie będzie nasłuchiwał Pound, który zajmie się rozkładaniem ruchu na pozostałe serwery. Plik konfiguracyjny dla Pound’a może wyglądać:


User "nobody"
ListenHTTP
  Address 0.0.0.0
  Port    9000
  Service
    BackEnd
      Address 127.0.0.1
      Port    9001
    End
    BackEnd
      Address 127.0.0.2
      Port    9002
    End
    BackEnd
      Address 127.0.0.3
      Port    9003
    End
    BackEnd
      Address 127.0.0.4
      Port    9004
    End
  End
End

Pozostało tylko uruchomić mongrela. Po zainstalowaniu (gem install mongrel), przechodzimy do folderu z naszą aplikacją railsów i odpalamy

mongrel_rails cluster::configure -e production \
    -p 9000 -N 4 -c /opt/ror/mojprojekt -a 127.0.0.1 \
    --user mongrel --group mongrel 

(Parametr -a chroni nas przed możliwością zewnętrznego odpalenia w przeglądarce Railsów działającej bezpośrednio na którymś z procesów mongrela).

Powinien powstać plik: config/mongrel_cluster.yml

---
port: "9000"
environment: production
address: 127.0.0.1
pid_file: log/mongrel.pid
servers: 4

Cluster odpalamy za pomocą

mongrel_rails cluster::start

Dodatkową zaletą używania mongrela jest możliwość wygodnego restartowania dowolnej aplikacji Railsów (a można ich mieć przecież wiele) bez konieczności restartowania serwera HTTP (mongrel_rails cluster::restart)

Poza tym mongrel świetnie nadaje się do zastąpienia Webricka, standardowego serwera HTTP zalecanego do tej pory do pracy developerskiej. Zamiast

ruby script/server
można użyć mongrela:
mongrel_rails start

Domyślnie podniesie się serwer HTTP na porcie 3000 i w trybie development (czyli identycznie jak Webrick). Mongrel jest jednak znacznie szybszy!

Dla użytkowników windozy też jest dobra wiadomość. Mongrel można zainstalować jako usługę windowsów i zarządzać standardowo tak jak inne usługi. Można np. odpalić sobie na jednym porcie Rails w trybie produkcyjnym, a na drugim – w trybie developerskim. W tym drugim wypadku nie trzeba restartować serwera bo zmiany w kodzie automatycznie przeładowują serwerek (tak jak Webrick)

Oczywiście to wszystko ślicznie działa z Capistrano, railsowym systemem automatycznie synchronizującym zmiany na n-serwerach.

Jak dla mnie, mongrel wnosi nową jakość do pracy z Railsami i warto przyjrzeć się mu dokładniej. Nie zdziwiłbym się, jakby następne wersje Railsów standardowo zalecały używanie mongrela, a nie Webricka.

Tak jak siłą Google są tysiące mniejszych maszyn a nie jedna – super szybka, tak nawet najszybszy serwer wyposażony w Apache i mod_python (który używa domyślnie Django) nie ma szans w starciu z clusterem Railsów opartym na procesach mongrel i korzystających z kilku serwerów.

Wykres z artykułu Scaling Rails with Apache 2.2, mod_proxy_balancer and Mongrel

-

1 Dlaczego nie wykorzystywany jest load balancer wbudowany w Lighttpd? Bo jest kiepsko zaimplementowany i są problemy z restartem procesów. Dlatego zalecany jest zewnętrzny load balancer lub użycie najnowszej wersji Apache 2.2 który ma ten moduł dobrze zaimplementowany.

Posted in  | Tagi  | 15 comments

Comments

  1. Avatar lopex powiedział about 2 hours later:

    Ponoć pen jest szybszy od pounda, z drugiej strony ten drugi jest wygodniejszy. W gemie Mongrela znajdują się dodatkowe recipes do capistrano plus skrypt startowy system V – wystarczy podlinkowac.

  2. Avatar lopex powiedział about 2 hours later:

    oops, w gemie mongrel_cluster

  3. Avatar Jarosław Zabiełło powiedział about 3 hours later:

    Muszę zobaczyć ten skrypt startowy. Nie wiedząc o tym, napisałem go sobie od zera. :)

  4. Avatar lopex powiedział about 4 hours later:

    Tutaj jest fajne howto: http://blog.codahale.com/2006/06/19/time-for-a-grown-up-server-rails-mongrel-apache-capistrano-and-you/ ;)

  5. Avatar hosiawak powiedział about 13 hours later:

    lighttpd w trunku svn’a posiada juz poprawna obsluge proxy, czyli dziala prawidłowo jako load balancer. Nie napisałeś jak dokładnie rozwiązać sprawę wysyłania statycznych stron do lighttpd. :) (Tutaj link ze szczegółami)

  6. Avatar Jarosław zabiełło powiedział about 15 hours later:

    Ależ napisałem. Przyjrzyj się dokładnie:

    ...
    $HTTP["url"] !~ "^/(cgi-bin|images|stylesheets|javascripts)" {
        proxy.server = ( "/" => ( ( "host" => "127.0.0.1" , "port" => 9000 ) ))
    }
    server.document-root = "/home/httpd/domena.host/public"
    ...

    Ten wpis mówi, że jeśli w url NIE występuje na początku żadne ze słów: cgi-bin, images, stylesheets czy javascripts, to wtedy ma przekazywać request do load balancera. Wszystkie inne requesty mają być obsługiwane przez Lighttpd (czyli ze ścieżki document.root)

    Co do lighttpd i svn, to wolę poczekać na wersję oficjalną.

  7. Avatar rsz powiedział about 16 hours later:

    To, że Symphony jest wolny, nie znaczy, że PHP jest wolniejszy od Rubiego (jest oczywiście odwrotnie). Zupełnie błędny i mylący czytelników wniosek!

  8. Avatar lopex powiedział about 17 hours later:

    a YARV się bardzo szybko rozwija ;)

  9. Avatar hosiawak powiedział 1 day later:

    Chciałem jedynie wskazać język CML używany w Lighttpd jakos mod_cml i zarządzanie keszowanymi statycznymi stron (page cache) które Railsy generują. Ale oczywiście można i tak jak napisałeś powyżej, żeby keszować tylko images/javascripts/stylesheets

  10. Avatar hosiawak powiedział 1 day later:

    To, że Symphony jest dużo wolniejszy od Railsów nawet z włączoną akceleracją to coś jednak oznacza (albo Symphony jest bardzo źle napisane albo w PHP nie da się pisać szybkich skomplikowanych systemów – osobiście skłaniam się do tego drugiego wniosku).

    Co do Rubiego i PHP – pewnie poszczególne funkcje PHP w porównaniu z odpowiednikami w Rubim są szybsze. Tylko komu zależy na pojedynczych funkcjach ? Zawsze można skorzystać z RubyInline dla jakiegoś krytycznego fragmentu kodu i napisać implementację w C :)

  11. Avatar lopex powiedział 1 day later:

    a to się w Rubym robi bajecznie prosto nawet bez swiga ;)

  12. Avatar Tomasz Czubinski powiedział 5 days later:

    Na poczatku tez mialem podobna konfiguracje. Mialem problemy z lighttpd mod_proxy i fast_cgi i rozwiazalem to nieco inaczej. Dzieki dobremu przykladowi z sieci. Jak znajde to wrzuce tutaj.

    Postawilem pound na 80 z BACK ENDami na 9000-4, nastepnie mongrel_cluster 9000-4 I wszystko dziala bez lighttpd.

    Jesli chodzi o lighttpd(bez proxy) to uzywam go do lzejszych statycznych spraw.

    Co myslicie o tym?

  13. Avatar Tomasz Czubinski powiedział 5 days later:

    Dodam jeszcze, ze w pound.cfg to mam na samym koncu.

    Service BackEnd Address 127.0.0.1 Port 3000 End End

    Dzieki temu reszta zostaje przekierowana na port 3000 na ktorym jest lighttpd, ktory obsluguje wszystko co statyczne.

    Zalozenie Pound jest byc na froncie i obslugiwac i filtorwac bezpiecznie http/https oczywiscie z balansowaniem. Taki szybki i zreczny doktor/mechanik :)

  14. Avatar sickill powiedział 6 months later:

    A ja z innej beczki. Jakiego softu uzyles do stworzenia tego diagramu? Wyglada to naprawde znakomicie i ciekaw jestem czy to cos komercyjnego czy jakos open-source.

  15. Avatar Jarosław Zabiełło powiedział 6 months later:

    W ogólne nie tworzyłem, podlinkowałem dynamicznie obrazek z angielskiego artykułu.

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz