Otwieram plik, który ma 100 000 adresów URL. Muszę wysłać żądanie HTTP do każdego adresu URL i wydrukować kod stanu. Używam Pythona 2.6 i do tej pory przyglądałem się wielu mylącym sposobom, w jakie Python implementuje wątkowanie / współbieżność. Zajrzałem nawet do biblioteki współbieżności w Pythonie , ale nie mogę dowiedzieć się, jak poprawnie napisać ten program. Czy ktoś napotkał podobny problem? Chyba na ogół muszę wiedzieć, jak wykonywać tysiące zadań w Pythonie tak szybko, jak to możliwe - przypuszczam, że oznacza to „jednocześnie”.
python
http
concurrency
Igor Ganapolski
źródło
źródło
requests.get
irequests.head
(tj. Żądanie strony kontra żądanie główne) zwrócenie różnych kodów stanu, więc nie jest to najlepsza radaOdpowiedzi:
Rozwiązanie Twistedless:
Ten jest nieco szybszy niż skręcone rozwiązanie i zużywa mniej procesora.
źródło
concurrent*2
?conn.close()
. Otwarcie zbyt wielu połączeń HTTP może w pewnym momencie zatrzymać skrypt i zjadać pamięć.Queue
modułu została zmieniona naqueue
Python 3. To jest kod Python 2.Rozwiązanie wykorzystujące asynchroniczną bibliotekę sieciową tornado
źródło
Wszystko zmieniło się nieco od 2010 roku, kiedy to zostało opublikowane i nie wypróbowałem wszystkich innych odpowiedzi, ale wypróbowałem kilka, i znalazłem, że działa najlepiej dla mnie za pomocą python3.6.
Udało mi się pobrać około ~ 150 unikalnych domen na sekundę działających na AWS.
źródło
time1 = time.time()
na górze pętli for itime2 = time.time()
zaraz po pętli for.Wątki absolutnie nie są tutaj odpowiedzią. Zapewnią zarówno wąskie gardła procesu, jak i jądra, a także ograniczenia przepustowości, które są nie do przyjęcia, jeśli ogólnym celem jest „najszybszy sposób”.
Trochę
twisted
i jego asynchronicznyHTTP
klient dałby znacznie lepsze wyniki.źródło
Wiem, że to stare pytanie, ale w Pythonie 3.7 możesz to zrobić za pomocą
asyncio
iaiohttp
.Możesz przeczytać więcej na ten temat i zobaczyć tutaj przykład .
źródło
urls= [fetch(construct_fetch_url(u),idx) for idx, u in enumerate(some_URI_list)]
results = await asyncio.gather(*urls)
Używaj grequestów , jest to kombinacja żądań + moduł Gevent.
GRequests pozwala na używanie żądań z Geventem do łatwego tworzenia asynchronicznych żądań HTTP.
Użycie jest proste:
Utwórz zestaw niewysłanych żądań:
Wyślij je wszystkie jednocześnie:
źródło
Dobrym podejściem do rozwiązania tego problemu jest najpierw napisanie kodu wymaganego do uzyskania jednego wyniku, a następnie włączenie kodu wątkowego w celu zrównoleglenia aplikacji.
W idealnym świecie oznaczałoby to po prostu jednoczesne uruchomienie 100 000 wątków, które wysyłają swoje wyniki do słownika lub listy do późniejszego przetworzenia, ale w praktyce masz ograniczoną liczbę równoległych żądań HTTP, które możesz wysłać w ten sposób. Lokalnie istnieją ograniczenia dotyczące liczby gniazd, które można jednocześnie otworzyć, liczby wątków wykonania, na które zezwoli interpreter języka Python. Zdalnie możesz mieć ograniczoną liczbę jednoczesnych połączeń, jeśli wszystkie żądania dotyczą jednego serwera lub wielu. Ograniczenia te prawdopodobnie będą wymagać napisania skryptu w taki sposób, aby w dowolnym momencie sondować tylko niewielką część adresów URL (100, jak wspomniano w innym plakacie, to prawdopodobnie przyzwoity rozmiar puli wątków, chociaż może się okazać, że może pomyślnie wdrożyć wiele innych).
Możesz wykonać ten wzór, aby rozwiązać powyższy problem:
list
lubdict
CPython, możesz bezpiecznie dołączyć lub wstawić unikalne elementy z wątków bez blokad , ale jeśli piszesz do pliku lub wymagasz bardziej złożonej interakcji między wątkami , powinieneś użyć blokada wzajemnego wykluczenia w celu ochrony tego stanu przed korupcją .Sugerowałbym użycie modułu wątków . Możesz go użyć do uruchamiania i śledzenia uruchomionych wątków. Obsługa wątków w Pythonie jest pusta, ale opis problemu sugeruje, że jest całkowicie wystarczający dla twoich potrzeb.
Wreszcie, jeśli chcesz zobaczyć całkiem proste stosowanie równoległej aplikacji sieciowej napisany w Pythonie, sprawdź ssh.py . To mała biblioteka, która wykorzystuje wątki w języku Python do równoległego łączenia wielu połączeń SSH. Projekt jest na tyle zbliżony do twoich wymagań, że możesz uznać go za dobry zasób.
źródło
Jeśli chcesz uzyskać najlepszą możliwą wydajność, możesz rozważyć użycie asynchronicznego we / wy zamiast wątków. Narzut związany z tysiącami wątków systemu operacyjnego nie jest trywialny, a przełączanie kontekstu w interprecie Pythona dodaje jeszcze więcej. Wątkowanie z pewnością wykona zadanie, ale podejrzewam, że trasa asynchroniczna zapewni lepszą ogólną wydajność.
W szczególności sugerowałbym asynchronicznego klienta WWW w bibliotece Twisted ( http://www.twistedmatrix.com ). Ma wprawdzie stromą krzywą uczenia się, ale jest dość łatwy w użyciu, gdy dobrze opanujesz styl programowania asynchronicznego Twisted.
Poradnik na temat asynchronicznego interfejsu API klienta WWW Twisted jest dostępny pod adresem:
http://twistedmatrix.com/documents/current/web/howto/client.html
źródło
Rozwiązanie:
Czas na test:
Pingtime:
źródło
Korzystanie z puli wątków jest dobrą opcją i znacznie to ułatwi. Niestety, python nie ma standardowej biblioteki, która sprawia, że pule wątków są wyjątkowo łatwe. Ale tutaj jest przyzwoita biblioteka, która powinna na początek: http://www.chrisarndt.de/projects/threadpool/
Przykładowy kod z ich strony:
Mam nadzieję że to pomoże.
źródło
q_size
> 0 rozmiar kolejki żądań pracy jest ograniczony, a pula wątków blokuje się, gdy kolejka jest pełna i próbuje umieścić w niej więcej żądań pracy (patrzputRequest
metoda), chyba że użyjesz równieżtimeout
wartości dodatniejputRequest
.”Tworzenie
epoll
obiektu,otworzyć wiele gniazd TCP klienta,
dostosować swoje bufory Prześlij być nieco więcej niż nagłówka żądania,
wysłać nagłówek żądania - powinna być natychmiastowa, po prostu umieszczając w buforze, zarejestruj gniazdo w
epoll
obiekcie,zrobić
.poll
naepoll
obect,czytać pierwszy 3 bajty z każdego gniazda z
.poll
,zapisz je,
sys.stdout
a następnie\n
(nie opróżnij), zamknij gniazdo klienta.Ogranicz liczbę gniazd otwieranych jednocześnie - radzić sobie z błędami podczas tworzenia gniazd. Utwórz nowe gniazdo tylko wtedy, gdy inne jest zamknięte.
Dostosuj limity systemu operacyjnego.
Spróbuj rozwidlić kilka (nielicznych) procesów: może to pomóc nieco bardziej efektywnie wykorzystać procesor.
źródło
W twoim przypadku wątki prawdopodobnie załatwią sprawę, ponieważ prawdopodobnie będziesz spędzać najwięcej czasu czekając na odpowiedź. W standardowej bibliotece znajdują się pomocne moduły, takie jak Kolejka, które mogą pomóc.
Zrobiłem podobnie z równoległym pobieraniem plików i było to dla mnie wystarczająco dobre, ale nie na skalę, o której mówisz.
Jeśli twoje zadanie było bardziej związane z procesorem, możesz spojrzeć na moduł wieloprocesowy , który pozwoli ci wykorzystać więcej procesorów / rdzeni / wątków (więcej procesów, które nie będą się wzajemnie blokować, ponieważ blokowanie jest na proces)
źródło
Rozważ użycie wiatraka , chociaż Windmill prawdopodobnie nie może zrobić tak wielu wątków.
Można to zrobić za pomocą ręcznie toczonego skryptu Python na 5 komputerach, z których każdy łączy dane wychodzące za pomocą portów 40000-60000, otwierając 100 000 połączeń portów.
Może także pomóc w przeprowadzeniu przykładowego testu z ładnie napisaną aplikacją QA, taką jak OpenSTA , aby dowiedzieć się, ile każdy serwer może obsłużyć.
Spróbuj także po prostu użyć prostego Perla z klasą LWP :: ConnCache. Prawdopodobnie uzyskasz w ten sposób większą wydajność (więcej połączeń).
źródło
Ten pokręcony klient sieciowy asynchroniczny działa dość szybko.
źródło
Przekonałem się, że użycie tego
tornado
pakietu jest najszybszym i najprostszym sposobem na osiągnięcie tego:źródło
Najprostszym sposobem byłoby użycie wbudowanej biblioteki wątków Pythona.
Nie są „prawdziwymi” wątkami jądra.Mają problemy (takie jak serializacja), ale są wystarczająco dobre. Chcesz kolejki i puli wątków. Jedną z opcji jest tutaj , ale to jest trywialne napisać własną. Nie możesz zrównoważyć wszystkich 100 000 połączeń, ale możesz zwolnić 100 (lub więcej) z nich jednocześnie.źródło