2012-05-09 10 views
9

Miałem aplikację Rails 3 na Nginx/Passenger, którą właśnie przeniosłem do Nginx/Thin (1.3.1). Jednak moja aplikacja jest teraz wyraźnie wolniejsza niż w przypadku Pasażera. Wiele wniosków również się kończy.Thin server underperforming/Jak działają uruchomione serwery WWW?

Thin to zdarzenie z serwerem internetowym. Z tego, co przeczytałem na temat zdarzających się serwerów internetowych, nie mają oni pojęcia pracowników. Jeden "pracownik" zajmuje się wszystkim. Jeśli więc jedna prośba oczekuje na IO, cienka przechodzi do następnej prośby i tak dalej. Jedno z wyjaśnień, jakie przeczytałem o zdarzeniach z serwerami, mówi, że serwery, które uległy awarii, powinny działać równie dobrze lub lepiej niż serwery pracownicze, ponieważ są one związane jedynie zasobami systemowymi.

Jednak moje użycie procesora jest bardzo małe. Moje użycie pamięci również jest bardzo niewielkie i nie ma zbyt wiele IO. Moja aplikacja wykonuje tylko kilka zapytań MySQL.

Jakie jest wąskie gardło? Czy mój cienki serwer nie powinien obsługiwać żądań, dopóki procesor nie osiągnie 100%? Czy muszę robić coś innego w mojej aplikacji, aby lepiej działała z uruchomionym serwerem?

Odpowiedz

13

Sergio jest poprawny. Twoja aplikacja jest prawdopodobnie lepsza od tradycyjnego modelu Apache/Passenger. Jeśli weźmiesz pod uwagę trasę, szczególnie na platformach jednowątkowych, takich jak Ruby, NIGDY nie możesz zablokować niczego, niezależnie od tego, czy jest to DB, serwery pamięci podręcznej, inne żądania HTTP, które możesz utworzyć - nic.

To sprawia, że ​​programowanie asynchroniczne (zdarzenie) jest trudniejsze - łatwo jest blokować pliki, zazwyczaj w formie synchronicznych dysków we/wy lub DNS. Nieblokujące (zdarzeniowe) frameworki, takie jak nodejs, są ostrożne, ponieważ (prawie) nigdy nie zapewniają wywołania funkcji ramowej, która jest blokowana, a raczej wszystko jest obsługiwane za pomocą wywołań zwrotnych (w tym zapytań DB).

To może być łatwiej wyobrazić, jeśli spojrzeć na sercu jednowątkowy non-blocking serwera:

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);  
     } 
    } 
} 

Co widać powyżej nazywa się pętla zdarzeń. wait_on_sockets jest zwykle dostarczany przez system operacyjny w formie wywołania systemowego, takiego jak select, poll, epoll lub kqueue. Jeśli processDataQuicklyWithoutBlocking trwa zbyt długo, bufor sieciowy aplikacji utrzymywany przez system operacyjny (nowe żądania, dane przychodzące itp.) Ostatecznie się zapełni i spowoduje odrzucenie nowych połączeń i przekroczenie limitu czasu istniejących, ponieważ $ socketsThatHaveActivity nie jest obsługiwane wystarczająco szybko . Różni się to od serwera z gwintem (np. Typowa instalacja Apache), w którym każde połączenie jest obsługiwane przy użyciu osobnego wątku/procesu, więc dane przychodzące będą odczytywane do aplikacji, gdy tylko nadejdą, a dane wychodzące będą wysyłane bezzwłocznie .

To, czego nie robią blokujące frameworki, takie jak nodejs, gdy robisz (na przykład) zapytanie DB, to dodaje połączenie gniazda serwera DB do monitorowanej listy gniazd ($ gniazd), więc nawet jeśli twoje zapytanie jakiś czas, twój (jedyny) wątek nie jest zablokowany na tym jednym gnieździe. Raczej stanowią one callback:

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

Jak widać powyżej, db.query wraca natychmiast, absolutnie bez blokowania na serwerze db ogóle.Oznacza to również, często trzeba pisać kodu takiego, chyba że język programowania sama obsługuje funkcje async (jak nowy C#):

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

Reguła nigdy-nigdy-blok może być nieco złagodzone, jeśli masz wiele procesów każdy z nich uruchamia pętlę zdarzeń (zazwyczaj sposób uruchamiania klastra węzłów) lub używa puli wątków do utrzymywania pętli zdarzeń (java, etc, możesz pisać własne w C/C++). Podczas gdy jeden wątek jest zablokowany, inne wątki nadal mogą wykonywać pętlę zdarzeń. Ale pod dużym obciążeniem nawet te nie zdołają wykonać. Więc NIGDY KIEDYKOLWIEK ZABLOKOWANY na zdarzeniu serwera.

Tak więc, jak widać, serwery, na których wystąpiły zdarzenia, zazwyczaj próbują rozwiązać inny problem - mogą mieć bardzo wiele otwartych połączeń. Gdzie są najlepsi, to po prostu przesuwanie bajtów za pomocą obliczeń świetlnych (np. Serwery komet, pamięci podręczne, takie jak memcached, lakier, serwery proxy, takie jak nginx, squid itd.). Nie ma nic wartego, mimo że skala jest lepsza, czasy reakcji generalnie rosną (nic nie jest lepsze niż rezerwowanie całego wątku dla połączenia). Oczywiście może nie być ekonomicznie/obliczeniowo wykonalna możliwość uruchomienia takiej samej liczby wątków, jak liczba współbieżnych połączeń.

Teraz wracam do problemu - chciałbym jeszcze raz polecić, abyście nadal utrzymywali Nginxa, ponieważ jest on doskonały w zarządzaniu połączeniami (co jest oparte na zdarzeniach) - zazwyczaj oznacza obsługę HTTP, SSL itp. Następnie należy podłączyć to do aplikacji Railsowej za pomocą FastCGI, w której nadal trzeba uruchamiać pracowników, ale nie trzeba przerabiać aplikacji, aby w pełni ją zawrzeć. Powinieneś także pozwolić Nginxowi na wyświetlanie statycznej zawartości - nie ma sensu zmuszać pracowników Rails do pracy z czymś, co zwykle Nginx robi lepiej. Takie podejście na ogół skaluje się znacznie lepiej niż Apache/Passenger, zwłaszcza jeśli prowadzisz witrynę o dużym natężeniu ruchu.

Jeśli możesz napisać całą swoją aplikację, aby ją wydobyć, to świetnie, ale nie mam pojęcia, jak łatwe i trudne jest to w Ruby.

+0

Wow .. dzięki za szczegółową odpowiedź Tejas. A więc benchmarki, które czytam w sieci ... czy to dla zupełnie innego gatunku aplikacji? Witryna Thina podaje aplikację rails jako przykładową aplikację dla cienkich. http://code.macournoyer.com/thin/. Odniosłem wrażenie, że mogę zastąpić pasażera cienką i wszystko będzie w dobrym humorze. –

+0

Dopóki nie blokujesz nigdzie, powinieneś móc odtworzyć te testy porównawcze. – tejas

3

Tak, Cienki wykonuje zdarzenie we/wy, ale tylko dla części HTTP. Oznacza to, że może odbierać przychodzące dane HTTP podczas przetwarzania żądania. Jednak wszystkie blokujące operacje we/wy, które wykonujesz podczas przetwarzania, nadal blokują. Jeśli twój MySQL nie zareaguje, wtedy kolejka Thin request zapełni się.

Aby uzyskać więcej informacji na temat serwera WWW, należy sprawdzić numer Rainbows.

+0

Witam Sergio. Wybacz moje zrozumienie pieszych tych koncepcji. Czytałem, że klejnot mysql2 na szynach również nie wydał IO. Ponadto, jeśli tak jest, czy serwer oparty na pracownikach nie byłby lepszy? Używam tylko 2 instancji cienkiego, w przeciwieństwie do 25 wystąpień pasażera, który wcześniej uruchomiłem. –

+0

@ CoffeeBite: to *** może *** wykonywać połączenia asynchroniczne, tak, ale to nie jest automatyczne i musisz napisać kod, aby to się stało. Domyślnie jest synchroniczny. –

+0

@ CoffeeBite: "Czy pracownicy nie zawsze będą lepsi" - nie jestem pewien. Ja sam używam [Unicorn] (http://unicorn.bogomips.org/) za nginxem. Nginx obsługuje HTTP I/O, a jednorożce szybko obsługują żądania. –

Powiązane problemy