Szybszy, asynchroniczny Mongrel z EventMachine lub Swiftiply
Posted by Jarosław Zabiełło Mon, 24 Dec 2007 13:21:00 GMT
Dużo się zmieniło od czasu zalecania używania Railsów w trybie FastCGI. To się nie sprawdziło i miejsce zestawu Lighttpd + FastCGI zajął Apache2/Nginx + proxy do Mongrel clustera. Z Mongrel clusterem jest jednak problem, że nie działa poza systemami uniksowymi. Poza tym udało mi się zaobserwować kilka razy pewną niestabilność dla mongrel clustera: czasami pojedynczy Mongrel się zawiesi i trzeba restartować cały ich klaster. Większą stabilność i dużo większą wydajność (szczególnie przy wielu równoległych żądaniach) zapewniać Mongrel działający z Swiftiply lub z EventMachine.
Swiftiply
Swiftiply jest reklamowany jako klasterowy proxy dla aplikacji internetowych. Korzysta z trwałych połączeń z klientami i można go skalować bez zmian w konfiguracji i restartów. Po prostu podpina się kolejny Mongrel z kolejnego komputera do tego samego adresu IP i zostaje automatycznie włączony do pętli obsługi zdarzeń. Dzięki Ezrze Zygmuntowiczowi, gem Swiftiply udostępnia też gotowy, przerobiony Mongrel działający zarówno z Swiftiply jak i EventMachine. Sam serwer Swiftiply może działać w ogóle bez zewnętrznego serwera HTTP, ale ci co cenią sobie max. wydajność pewnie i tak będą chcieli na froncie postawić szybkiego Nginxa. Wystarczy spiąć go przez mod_proxy do jednego (sic!) Mongrela odpalonego w trybie Swiftiply.
EventMachine
EventMachine to biblioteka napisana w C dla programów Rubiego, Javy i C++. Służy do obsługi zdarzeń w sposób asynchroniczny zamiast wielowątkowy (podobnie jak to robi pythonowy Twisted). EventMachine jest biblioteką bardzo szybką. Serwer pracujący asynchronicznie nie wpada w blokady i jest często szybszy od serwerów pracujących wielowątkowo.
Zamiast tworzyć wiele procesów Mongrela aby zapewnić równoległą pracę dla Rails, wystarczy odpalić jeden (sic!) proces Mongrela asynchronicznego. Asynchroniczny Mongrel działa zarówno z Railsami jaki Merbem. Nie muszę mówić, jakie są oszczędności na pamięci w porównaniu do klastra procesów Mongrela który zwykle zaleca się używać dla Railsów! Z prostych testów porównawczych wyszło mi że Mongrel asynchroniczny jest jakieś dwa razy szybszy od Mongrela wielowątkowego. (Zobacz też Mongrel vs Evented Mongrel)
Jak to skonfigurować?
Po zainstalowaniu gemu Swiftiply stary skrypt mongrel_rails zostaje zastąpiony nowszym (ale tak, że jest zgodny ze starym mongrelem wielowątkowym). Sterowanie trybem pracy dokonywane jest przez ustawienie stosownej zmiennej środowiskowej. Np. aby odpalić Mongrela w trybie asynchronicznym wystarczy napisać
Poniżej prosty skrypt startujący jaki napisałem dla Ubuntu (wszystkie opcje wyświetli polecenie mongrel_rails start -h@)
#!/usr/bin/ruby
ADDR='127.0.0.1'
PORT=7100
# Ścieżka do projektu RoR:
ROOT='/home/app/rails/homepage/current'
PID="#{ROOT}/log/blog.pid"
USER='www-data'
GROUP='www-data'
APPL='/usr/bin/mongrel_rails'
cmd_start = "env EVENT=1 #{APPL} start -a #{ADDR} -p #{PORT} --user #{USER} --group #{GROUP} -P #{PID} -e production -d"
cmd_stop = "#{APPL} stop -P #{PID}"
curdir = Dir.pwd
Dir.chdir ROOT
case ARGV.first
when 'start'
system cmd_start
when 'stop'
system cmd_stop if File.exists?(PID)
when 'restart'
system cmd_stop if File.exists?(PID)
system cmd_start
else
puts "Usage: sudo #{__FILE__} {start|stop|restart}"
end
Dir.chdir curdirJeszcze tylko komenda
cd /etc/init.d
update-rc.d -f mogrel_home.rb defaultsi Ubuntu będzie automatycznie ten skrypt uruchamiał podczas restartu maszyny.
Odnośnie Nginxa to nie trzeba wiele zmieniać poza tym, aby skierować proxy na jeden port (w tym wypadku 7100)
upstream mongrel_blog {
server 127.0.0.1:7100;
}
server {
# ... inne opcje
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect false;
# ... reszta opcji
if (!-f $request_filename) {
proxy_pass http://mongrel_blog;
break;
}
}
}Stary mongrel_cluster doczekał się konkurencji w postaci gema mongrel_cow_cluster który również powinien szybciej działać od mongrel_cluster’a. Nawet zwolennicy Webricka mogą sobie doinstalować coś szybszego od domyślnego Webricka – forkowy webrick-high-performance (tylko systemy uniksowe). Ale wydaje mi się, że najlepszym rozwiązaniem jest Mongrel asynchroniczny.
Zobacz też entuzjastyczną opinię Ezry Zygmuntowicza o event Mongrelu i o Swiftiply.


Kanały IRC![[Dilber w Onecie]](/images/larry.png)


Spróbowałem. W porównnaiu do zwykłego, pojedyńczego mongrela wydaje się wolniejszy. Chodzi o to że po w pisaniu urla trzeba było czekać nawet sekunde dwie zanim coś wogóle zaczęło się dziać. Nie prowadziłem żadnych benchmarków, w logach req/sec się zbytnio nie zmieniły, ale to wrażenie dla odwiedzającego wydaje mi się gorsze.
A co to za test “wpisać urla”? :) Zapuść przynajmniej
ab -n 1000 -c 10albo coś podobnego. Ezra stwierdza że event Mongrel chodzi nie tylko wyraźnie szybciej ale i zużywa też mniej pamięci. Nie wiem jak duży zysk jest w RoR, ale w Merb jest spory.Musiałem trochę przebudować treść artykułu, bo był trochę niejasny i wkradły się błędy w listingu.
Widzę, że jesteś entuzjastą Ruby. To co piszesz bardzo fajnie się czyta ale na Twoim miejscu byłbym ostrożniejszy w opisywaniu technologii. Mongrel/Swiftiply są bardzo niestabilne przy dużych obciążeniach (idle ~5%) i jeśli ktoś je użyje na podstawie takich entuzjastycznych tekstów może ładnie się naciąć.
Pozdrawiam.
a ten wynalazek?
http://www.rubyinside.com/thin-a-ruby-http-daemon-thats-faster-than-mongrel-688.html
?? jest jeszcze szybszy podobno :)
No wlasnie, piszesz, ze tandem apache+mod_proxy+mongrel_cluster sie nie sprawdzil? Co sie dzialo? Wieszal sie?
Bo chyba mam to samo – po restarcie mongrel_cluster wszystko jest ok, ale tak przez pare godzin… Potem apache sie nie moze podlaczyc do zadnego z trzech uruchomionych mongreli. Netstat pokazuje caly czas otwarte porty.
Czy taki sam efekt miales?
Pzdr.
@exor: Miałem coś podobnego, nie wiem czy to samo. Pojedyńcze mongrele czasem padały w clusterze, znikały procesy. W efekcie, przy zastosowanym load balancingu, na kilka requestów niektóre wywalały błąd.