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.
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. –
Dopóki nie blokujesz nigdzie, powinieneś móc odtworzyć te testy porównawcze. – tejas