2011-11-16 19 views
8

Przeczytałem mnóstwo materiałów w Internecie na temat bezpieczeństwa i wydajności wątków w różnych wersjach ruby ​​i szyn i myślę, że rozumiem te rzeczy całkiem dobrze w tym momencie.Jak wdrożyć asynchroniczną aplikację szyny wątków?

To, co wydaje się dziwnie omijać podczas dyskusji, to jak wdrożyć asynchroniczną aplikację Rails. Mówiąc o wątków i synchronizacji w aplikacji, są dwie rzeczy, które ludzie chcą zoptymalizować:

  1. wykorzystaniu wszystkich rdzeni procesora z wykorzystaniem minimalny RAM
  2. mogąc służyć nowych żądań podczas gdy poprzednie wnioski czekają na IO

Punkt 1 to miejsce, w którym ludzie (słusznie) są podekscytowani JRuby. Na to pytanie mam tylko próby optymalizacji pkt 2.

powiedzieć, jest to jedyny kontroler w moim app:

TheController < ActionController::Base 
    def fast 
    render :text => "hello" 
    end 

    def slow 
    render :text => User.count.to_s 
    end 
end 

fast ma IO i może służyć setek lub tysięcy zapytań na sekundę, a slow musi przesłać żądanie przez sieć, czekać na wykonanie pracy, a następnie odebrać odpowiedź przez sieć, a zatem jest znacznie wolniejsza niż fast.

Idealne wdrożenie umożliwiłoby spełnienie setek zgłoszeń do fast, podczas gdy żądanie na slow czeka na IO.

To, co wydaje się brakować w dyskusjach w Internecie, to, która warstwa stosu jest odpowiedzialna za włączenie tej współbieżności. thin ma flagę --threaded, która będzie "wywoływała aplikację Rack w wątkach [eksperymentalnych]" - czy to uruchamia nowy wątek dla każdego przychodzącego żądania? Buforuje instancje aplikacji szafy w wątkach, które utrzymują się i czekają na przychodzące żądania?

Czy cienki jest jedyny sposób, czy są inne? Czy środowisko wykonawcze ruby ​​ma znaczenie dla optymalizacji punktu 2?

+0

Wątpię, aby twoja wersja rubinowa miała dużo do powiedzenia w odniesieniu do punktu 2. Byłaby to bardziej zależna od implementacji współbieżności serwera i od tego, jak układane są szyny. – providence

Odpowiedz

6

Właściwe podejście do Ciebie zależy w dużym stopniu od tego, co robi Twoja metoda slow.

W idealnym świecie można użyć czegoś podobnego do klejnotu sinatra-synchrony, aby obsłużyć każde żądanie w światłowodzie. Ograniczałbyś się tylko do maksymalnej liczby włókien. Niestety, rozmiar stosu na włóknach to hardcoded i jest łatwy do przekroczenia w aplikacji Rails. Dodatkowo przeczytałem kilka horrorów o trudnościach w debugowaniu włókien, ze względu na automatyczną wydajność po inicjacji asynchronicznej IO. Podczas korzystania z włókien warunki wyścigu są nadal możliwe. Obecnie sfilmowana Ruby to trochę getto, przynajmniej na początku aplikacji internetowej.

Bardziej pragmatyczne rozwiązanie, które nie wymaga zmian kodu, to użycie serwera szafy, który ma pulę wątków roboczych, takich jak Rainbows! lub Puma. Wierzę, że flaga Thin's --threaded obsługuje każde żądanie w nowym wątku, ale kręcenie natywnego wątku systemu operacyjnego nie jest tanie. Lepiej używać puli wątków z odpowiednio ustawionym rozmiarem puli. W Railsach nie zapomnij ustawić config.threadsafe! w produkcji.

Jeśli nie masz nic przeciwko zmianie kodu, możesz sprawdzić doskonałego talk on real-time Rack Konstantina Haase'a. Omawia użycie klasy EventMachine::Deferrable w celu wygenerowania odpowiedzi poza tradycyjnym cyklem żądania/odpowiedzi, na którym zbudowany jest Rack. Wydaje się to naprawdę miłe, ale musisz przepisać kod w stylu asynchronicznym.

Zobacz także Cramp i Goliath. Pozwalają one na implementację metody slow w oddzielnej aplikacji Rack, która jest hostowana wraz z aplikacją Rails, ale prawdopodobnie będziesz musiał przepisać swój kod, aby działał również w modułach obsługi Cramp/Goliath.

Jeśli chodzi o twoje pytanie dotyczące środowiska wykonawczego Ruby, zależy to również od pracy, którą wykonuje slow. Jeśli wykonujesz obliczenia ciężkimi procesorami, ryzykujesz, że GIL spowoduje problemy. Jeśli robisz IO, to GIL nie powinien przeszkadzać w uzyskaniu. (Mówię, nie powinienem, ponieważ uważam, że czytałem o problemach ze starszym klejnotem mysql blokującym GIL.)

Osobiście osiągnąłem sukces dzięki synchronizacji z sinatrą dla zaplecza, usługi mashup. Mogę równolegle wysyłać kilka żądań do zewnętrznych usług sieci Web i czekać na ich powrót. Tymczasem serwer Rails Frontend korzysta z puli wątków i wysyła żądania bezpośrednio do backendu. Nie jest idealny, ale teraz działa wystarczająco dobrze.

Powiązane problemy