Mam projekt 10K LOC napisany w Django z całkiem sporą ilością selera ( RabbitMQ ) do zadań asynchronicznych i zadań w tle tam, gdzie to konieczne, i doszedłem do wniosku, że niektóre części systemu skorzystałyby na przepisaniu na coś innego niż Django dla lepszej współbieżności . Powody obejmują:
- Obsługa sygnałów i obiekty zmienne. Zwłaszcza, gdy jeden sygnał wyzwala inny, obsługa ich w Django za pomocą ORM może być zaskakująca, gdy instancje zmieniają się lub znikają. Chcę zastosować metodę przesyłania wiadomości, w której przekazywane dane nie zmieniają się w module obsługi ( podejście kopiowania przy zapisie Clojure wydaje się być dobre, jeśli mam rację).
- Części systemu nie są oparte na sieci Web i wymagają lepszego wsparcia w zakresie wykonywania zadań jednocześnie. Na przykład, system odczytuje tagi NFC , a gdy zostanie odczytany, dioda LED zaświeci się na kilka sekund (zadanie Celery), odtwarzany jest dźwięk (inne zadanie Celery), a baza danych jest odpytywana (inne zadanie). Jest to zaimplementowane jako polecenie zarządzania Django, ale Django i jego ORM są z natury synchroniczne i współużytkują pamięć ograniczają (myślimy o dodaniu większej liczby czytników NFC i nie sądzę, aby podejście Django + Celery działało dłużej, Chciałbym zobaczyć lepsze możliwości przekazywania wiadomości).
Jakie są zalety i wady korzystania z czegoś takiego jak Twisted lub Tornado w porównaniu z wyborem języka takiego jak Erlang lub Clojure ? Interesują mnie praktyczne korzyści i szkody.
Jak doszedłeś do wniosku, że niektóre części systemu wypadłyby lepiej w innym języku? Czy masz problemy z wydajnością? Jak poważne są te problemy? Jeśli może być szybszy, czy konieczne jest, aby był szybszy?
Przykład 1: Django w pracy poza żądaniem HTTP:
- Tag NFC jest czytany.
- Baza danych (i ewentualnie LDAP) jest odpytywana, a my chcemy coś zrobić, gdy dane staną się dostępne (czerwone lub zielone światło, odtwórz dźwięk). To blokuje za pomocą Django ORM, ale dopóki są dostępni pracownicy Selera, nie ma to znaczenia. Może być problem z większą liczbą stacji.
Przykład 2: „przekazywanie wiadomości” przy użyciu sygnałów Django:
post_delete
Zdarzenie jest przetwarzane inne przedmioty mogą być zmieniane lub usuwane z tego powodu.- Na koniec powiadomienia powinny być wysyłane do użytkowników. Byłoby dobrze, gdyby argumenty przekazane do modułu obsługi powiadomień były kopiami usuniętych lub przeznaczonych do usunięcia obiektów i gwarantowały, że nie zostaną zmienione w module obsługi. (Można to zrobić ręcznie, po prostu nie przekazując obiektom zarządzanym przez ORM do programów obsługi.)
źródło
Odpowiedzi:
Myśli otwierające
Jak doszedłeś do wniosku, że niektóre części systemu wypadłyby lepiej w innym języku? Czy masz problemy z wydajnością? Jak poważne są te problemy? Jeśli może być szybszy, czy konieczne jest, aby był szybszy?
Asynchronia jednowątkowa
Istnieje kilka pytań i innych zasobów internetowych, które już zajmują się różnicami, zaletami i wadami asynchronii jednowątkowej vs. współbieżności wielu wątków. Interesujące jest przeczytanie o tym, jak działa jednowątkowy model asynchroniczny Node.js, gdy we / wy jest głównym wąskim gardłem, a jednocześnie obsługiwanych jest wiele żądań.
Twisted, Tornado i inne modele asynchroniczne doskonale wykorzystują jeden wątek. Ponieważ wiele programów internetowych ma wiele operacji we / wy (sieć, baza danych itp.), Czas oczekiwania na połączenia zdalne znacznie się zwiększa. To czas, który można poświęcić na wykonywanie innych czynności - takich jak uruchamianie innych wywołań bazy danych, renderowanie stron i generowanie danych. Wykorzystanie tego pojedynczego wątku jest niezwykle wysokie.
Jedną z największych zalet asynchronii jednowątkowej jest to, że zużywa ona znacznie mniej pamięci. W przypadku wykonywania wielu wątków każdy wątek wymaga pewnej ilości zarezerwowanej pamięci. Wraz ze wzrostem liczby wątków rośnie również ilość pamięci wymaganej tylko do istnienia wątków. Ponieważ pamięć jest skończona, oznacza to, że istnieją ograniczenia dotyczące liczby wątków, które można utworzyć w dowolnym momencie.
Przykład
W przypadku serwera WWW udawaj, że każde żądanie ma swój własny wątek. Powiedz, że 1 MB pamięci jest wymagany na każdy wątek, a serwer WWW ma 2 GB pamięci RAM. Ten serwer sieciowy byłby w stanie przetworzyć (w przybliżeniu) 2000 żądań w dowolnym momencie, zanim zabraknie pamięci do przetworzenia.
Jeśli obciążenie jest znacznie wyższe, żądania zajmą bardzo dużo czasu (czekając na zakończenie starszych wniosków) lub będziesz musiał wrzucić więcej serwerów do klastra, aby zwiększyć liczbę możliwych jednoczesnych żądań .
Współbieżność wielu wątków
Współbieżność wielowątkowa polega natomiast na wykonywaniu kilku zadań jednocześnie. Oznacza to, że jeśli wątek zostanie zablokowany w oczekiwaniu na wywołanie bazy danych w celu powrotu, inne żądania mogą być przetwarzane w tym samym czasie. Wykorzystanie wątku jest niższe, ale liczba wykonanych wątków jest znacznie większa.
Kod wielowątkowy jest również znacznie trudniejszy do uzasadnienia. Występują problemy z blokowaniem, synchronizacją i innymi problemami dotyczącymi współbieżności. Asynchronia jednowątkowa nie ma tych samych problemów.
Kod wielowątkowy jest jednak znacznie wydajniejszy w przypadku zadań intensywnie wykorzystujących procesor . Jeśli nie ma możliwości, aby wątek „ustąpił” - tak jak połączenie sieciowe, które normalnie by blokowało - model z jednym wątkiem po prostu nie będzie miał żadnej współbieżności.
Oba mogą współistnieć
Oczywiście oba te elementy nakładają się; nie wykluczają się wzajemnie. Na przykład kod wielowątkowy można zapisać w sposób nieblokujący, aby lepiej wykorzystać każdy wątek.
Dolna linia
Jest wiele innych kwestii do rozważenia, ale lubię myśleć o dwóch takich:
W konkretnym przypadku musisz ustalić, jaki rodzaj pracy asynchronicznej jest wykonywany i jak często te zadania powstają.
Nie ma prostej odpowiedzi. Musisz rozważyć, jakie są twoje przypadki użycia i odpowiednio zaprojektować. Czasami lepszy jest asynchroniczny model jednowątkowy. Innym razem wymagane jest użycie wielu wątków w celu osiągnięcia masowego przetwarzania równoległego.
Inne uwagi
Należy również wziąć pod uwagę inne kwestie, a nie tylko wybrany model współbieżności. Czy znasz Erlang lub Clojure? Czy uważasz, że byłbyś w stanie napisać bezpieczny wielowątkowy kod w jednym z tych języków, aby poprawić wydajność swojej aplikacji? Czy przygotowanie jednego z tych języków zajmie dużo czasu i czy język, którego się uczysz, będzie korzystny w przyszłości?
Co powiesz na trudności związane z komunikacją między tymi dwoma systemami? Czy utrzymywanie równolegle dwóch oddzielnych systemów będzie zbyt skomplikowane? W jaki sposób system Erlang odbierze zadania od Django? W jaki sposób Erlang przekaże te wyniki z powrotem do Django? Czy wydajność jest na tyle istotnym problemem, że dodatkowa złożoność jest tego warta?
Końcowe przemyślenia
Zawsze uważałem, że Django jest wystarczająco szybkie i jest używane przez niektóre witryny o dużym natężeniu ruchu. Istnieje kilka optymalizacji wydajności, które można wykonać w celu zwiększenia liczby jednoczesnych żądań i czasu odpowiedzi. Trzeba przyznać, że do tej pory nic nie robiłem z Selerem, więc zwykłe optymalizacje wydajności prawdopodobnie nie rozwiążą żadnych problemów związanych z tymi asynchronicznymi zadaniami.
Oczywiście zawsze pojawia się sugestia, aby rzucić więcej sprzętu na problem. Czy koszt zapewnienia nowego serwera jest tańszy niż koszt opracowania i utrzymania całkowicie nowego podsystemu?
W tym momencie zadałem zbyt wiele pytań, ale taka była moja intencja. Odpowiedź nie będzie łatwa bez analizy i dalszych szczegółów. Jednak umiejętność analizowania problemów sprowadza się do znajomości pytań, które należy zadać, ale… mam nadzieję, że pomogłem na tym froncie.
Moje przeczucie mówi, że przepisywanie w innym języku nie jest konieczne. Złożoność i koszt będą prawdopodobnie zbyt duże.
Edytować
Odpowiedź na działania następcze
Twoje działania następcze przedstawiają kilka bardzo interesujących przypadków użycia.
1. Django działa poza żądaniami HTTP
Twój pierwszy przykład obejmował odczyt tagów NFC, a następnie przeszukanie bazy danych. Nie sądzę, aby pisanie tej części w innym języku było dla ciebie przydatne, ponieważ zapytania do bazy danych lub serwera LDAP będą ograniczone przez sieciowe operacje we / wy (i potencjalnie wydajność bazy danych). Z drugiej strony liczba równoczesnych żądań będzie ograniczona przez sam serwer, ponieważ każde polecenie zarządzania będzie uruchamiane jako osobny proces. Nastąpi czas konfiguracji i porzucania, który wpływa na wydajność, ponieważ nie wysyłasz wiadomości do już uruchomionego procesu. Będziesz jednak mógł wysyłać wiele żądań jednocześnie, ponieważ każde z nich będzie procesem izolowanym.
W tym przypadku widzę dwie ścieżki, które możesz zbadać:
'OPTIONS': {'threaded':True}
.) Mogą istnieć podobne opcje konfiguracji na poziomie bazy danych lub na poziomie Django, które można dostosować do własnej bazy danych. Bez względu na język, w którym piszesz zapytania do bazy danych, będziesz musiał poczekać na powrót tych danych, zanim zaświecisz diody LED. Wydajność kodu zapytania może jednak mieć znaczenie, a ORM Django nie jest błyskawiczny ( ale zazwyczaj wystarczająco szybki).Nie jestem pewien, jakiego serwera używasz dla Django.
mod_wsgi
dla Apache pozwala skonfigurować liczbę procesów i wątków w procesach, które obsługują żądania. Koniecznie dostosuj odpowiednią konfigurację serwera WWW, aby zoptymalizować liczbę żądań serwisowych.2. „Przekazywanie wiadomości” za pomocą sygnałów Django
Drugi przypadek użycia jest również dość interesujący; Nie jestem pewien, czy mam na to odpowiedzi. Jeśli usuwasz instancje modelu i chcesz operować na nich później, może być możliwe ich serializowanie,
JSON.dumps
a następnie deserializacjaJSON.loads
. Ponowne odtworzenie grafu obiektowego później (odpytywanie modeli pokrewnych) będzie niemożliwe, ponieważ powiązane pola są wczytywane z bazy danych leniwie, a to łącze już nie istnieje.Inną opcją byłoby jakoś oznaczyć obiekt do usunięcia i usunąć go tylko na końcu cyklu żądania / odpowiedzi (po obsłużeniu wszystkich sygnałów). Może to wymagać niestandardowego sygnału do wdrożenia tego, zamiast polegać na
post_delete
.źródło
Zrobiłem bardzo wyrafinowane wysoce skalowalne opracowanie dla dużego amerykańskiego dostawcy usług internetowych . Zrobiliśmy kilka poważnych transakcji przy użyciu serwera Twisted i koszmarem złożoności było sprawienie, aby Python / Twisted skalował się na wszystko, co było związane z procesorem . Ograniczenie wejścia / wyjścia nie stanowi problemu, ale ograniczenie procesora było niemożliwe. Mogliśmy szybko połączyć systemy, ale doprowadzenie ich do skalowania do milionów współbieżnych użytkowników było koszmarem konfiguracji i złożoności, gdyby były one związane z procesorem.
Napisałem o tym post na blogu, Python / Twisted VS Erlang / OTP .
TLDR; Erlang wygrał.
źródło
Praktyczne problemy z Twisted (które uwielbiam i używałem od około pięciu lat):
Zrobiłem trochę pracy przy użyciu Node.js z CoffeeScript i jeśli zależy Ci na równoczesnej wydajności, może to być warte skoku.
Czy zastanawiałeś się nad uruchomieniem wielu instancji Django z pewnymi ustaleniami, aby rozdzielić klientów między instancje?
źródło
Zasugeruję następujące, zanim rozważysz przejście na inny język.
select
), który jest dobry dla I / O.Nie używałbym wątków w Pythonie, gdy aplikacja ma priorytet pod względem wydajności. Wybrałbym powyższą opcję, która może rozwiązać wiele problemów, takich jak ponowne użycie oprogramowania, łączność z Django , wydajność, łatwość programowania itp.
źródło