2009-08-18 4 views
16

Pracuję nad kodem Pythona wzorowanym na serwerze prefabrykacji MPM Apache. Jestem bardziej programistą aplikacji niż programistą sieciowym i minęło 10 lat, odkąd przeczytałem Stevensa, więc staram się szybko zrozumieć kod.accept() z gniazdami współdzielonymi między wieloma procesami (w oparciu o prefinging Apache)

Znalazłem krótki opis how Apache's prefork code works, by Sander Temme.

Proces macierzysty, który zwykle działa jako pierwiastka, wiąże się z gniazdem (zwykle portu 80 lub 443). Tworzy potomka, który dziedziczy otwarty deskryptor pliku dla gniazda i zmienia identyfikator UID oraz gid na nieprzyznawalnego użytkownika i grupę . Dzieci konstruują zestaw zbiorczy deskryptorów plików detektorów (jeśli jest więcej niż jeden detektor) i obserwują aktywność na nim/nich. Jeśli zostanie znaleziona aktywność, dziecko wywoła accept() na aktywnym gnieździe i obsługuje połączenie. Kiedy zostanie to zrobione, powraca do oglądania zestawu danych pollset (lub listenera deskryptora).

Ponieważ wiele dzieci jest aktywnych i wszystkie odziedziczyły te same deskryptory plików gniazd, będą one oglądać ten sam zestaw pytań. Akceptowany muteks pozwala tylko jednemu dziecku oglądać zestaw ankiet, , a po znalezieniu aktywnego gniazda odblokuje muteksa, aby następne dziecko mogło zacząć oglądać zestawienie. Jeśli istnieje tylko jeden detektor , akceptowany muteks nie jest używany i wszystkie dzieci będą zawieszone w accept().

W ten sposób kod, na który patrzę, działa, ale nie rozumiem kilku rzeczy.

1) Jaka jest różnica między "dzieckiem" a "słuchaczem"? Myślałem, że każde dziecko jest słuchaczem, co jest zgodne z kodem, którego szukam, ale w opisie Temme może być "jeden słuchacz" i "dzieci". Kiedy dziecko ma wielu słuchaczy?

2) (w odniesieniu do 1) Czy jest to mutex dla jednego procesu lub system mutex? Jeśli o to chodzi, dlaczego mam muteks? Nie przyjmuje (2) własnego muteksu dla wszystkich słuchaczy? Moje badania mówią, że potrzebuję muteksu i że muteks musi być w całym systemie. (Stado, semaforów itp)

Temme mówi dalej:

rekord

Dzieci w pamięci współdzielonej obszaru (tabela wyników), gdy trwają serwowane żądanie. Bezczynne dzieci mogą zostać zabite przez proces nadrzędny na spełniające MaxSpareServers. Jeśli zbyt mało dzieci będzie w stanie bezczynności, rodzic będzie odradzać dzieci w celu zaspokojenia MinSpareServers.

3) Czy istnieje dobry kod odniesienia dla tej implementacji (najlepiej w języku Python)? Znalazłem Perla w wersji Net::Server::Prefork, która używa potoków zamiast pamięci współdzielonej do tablicy wyników. Znalazłem artykuł napisany przez Randal Schwartz, który wykonuje tylko preforking, ale nie robi tablicy wyników.

pre-fork example from the Perl Cookbook nie ma żadnego rodzaju blokady wokół select, a Chris Siebenmann's Python example mówi, że jest oparty na Apache, ale używa sparowanych gniazd do tablicy wyników, a nie wspólnej pamięci i używa gniazd dla kontroli, zawiera kontrolę dla danego dziecka do 'zaakceptować. To w ogóle nie pasuje do opisu Apache.

+0

Czy używasz czegoś w stylu 'mod_wsgi' jako interfejsu między Apache i Python? Jeśli tak, to powinno załatwić to wszystko dla ciebie. –

+0

To jest dla czystego serwera WSGI z prefixingiem Pythona. Mój klient chce lekkiego rozwiązania dla miejsc, które nie chcą Apache i mod_wsgi, lub odpowiednika. Jedynym serwerem WSGI tylko dla Pythona, który znalazłem, było Rozrastanie się, a to wymaga zdarzenia. ... Chociaż teraz stwierdziłem, że flup ma implementację taką jak Siebenmanna, która używa potoków dla tablicy wyników zamiast pamięci współdzielonej i z dopuszczalną licencją dla mojego klienta. –

Odpowiedz

15

W odniesieniu do (1) słuchacz jest jedynie odniesieniem do istnienia gniazda, na którym można odbierać połączenia. Ponieważ Apache może akceptować połączenia na wielu gniazdach w tym samym czasie, np. 80/443, jest wiele gniazd nasłuchujących. Każdy proces potomny będzie musiał nasłuchiwać na wszystkich tych gniazdach, gdy nadejdzie czas. Ponieważ accept() może być wykonane tylko na jednym gnieździe na raz, jest poprzedzone sondowaniem/select, więc wiadomo, na którym gnieździe słuchacza powinna być wykonana akceptacja.

W odniesieniu do (2) jest to globalny lub krzyżowy proces mutex. Oznacza to, że jedno zamknięcie procesu zablokuje inne procesy próbujące uzyskać tę samą blokadę. Chociaż accept() będzie technicznie serializować procesy, obecność wielu gniazd nasłuchujących oznacza, że ​​nie można na nich polegać, ponieważ nie wiesz wcześniej, które gniazdo do wykonania akceptować. Nawet w przypadku pojedynczego gniazda odbiornika, powodem akceptowania muteksu jest to, że jeśli istnieje duża liczba procesów obsługujących żądania, wówczas może to być dość kosztowne, jeśli system operacyjny przebudzi wszystkie procesy, aby zobaczyć, które następnie zwróciło dla niego(). Ponieważ Apache w trybie prefork może mieć ponad 100 procesów, może to spowodować problem.

Tak więc, jeśli masz tylko jedno gniazdo nasłuchiwania i wiesz, że masz tylko kilka procesów, które chcą wykonać wywołanie accept(), to prawdopodobnie możesz zrezygnować z procesu cross akceptacji mutex.

+0

Dzięki Graham! Całkowicie nie pomyślałem o tym, jak może być wielu słuchaczy i jak to wpłynie na wszystko. Moje zrozumienie jest teraz o wiele jaśniejsze, zarówno z tego, dlaczego kod, z którego korzystałem, działał, jak i dlaczego wyglądało na to, że nie działa. –

Powiązane problemy