2013-11-20 16 views
6

Pomysł jest prosty: muszę wysyłać wiele żądań HTTP równolegle.Jaki jest najlepszy sposób wysyłania wielu żądań HTTP w Pythonie 3?

Zdecydowałem się użyć do tego biblioteki requests-futures, która zasadniczo odradza wiele wątków.

Teraz mam około 200 żądań i wciąż jest dość powolny (zajmuje około 12 sekund na moim laptopie). Używam również wywołania zwrotnego, aby przeanalizować odpowiedź json (zgodnie z sugestią zawartą w dokumentacji biblioteki). Czy istnieje pewna reguła pozwalająca ustalić optymalną liczbę wątków w zależności od liczby żądań, czy istnieje?

W zasadzie zastanawiałem się, czy mogę przyspieszyć te prośby.

+0

Jaką wersję python? Twoje opcje stdlib zmieniają się dramatycznie od 2,7 do 3,3. – roippi

+0

Chciałbym zasugerować moduł Urllib + Threading, ale pakiet, z którym się łączysz, działa w zasadzie tak samo. Jeśli chodzi o dostrajanie liczby wątków, udało mi się uruchomić mniej niż 25 na moim laptopie (MacBook Pro, procesor 3,2 GHz, 16 GB pamięci RAM). – BenDundee

+0

@roippi Używam Pythona 3.3 –

Odpowiedz

6

Ponieważ używasz Pythona 3.3, polecam rozwiązanie stdlib, którego nie znajdziesz w połączonym wątku przez @ njzk2: concurrent.futures.

To jest wyższy poziom niż tylko prymitywów threading lub multiprocessing. Otrzymasz interfejs Executor do obsługi pulowania i raportowania asynchronicznego.

Docs mają przykład, który jest w zasadzie bezpośrednie zastosowanie do sytuacji, więc ja po prostu upuścić go tutaj:

import concurrent.futures 
import urllib.request 

URLS = #[some list of urls] 

# Retrieve a single page and report the url and contents 
def load_url(url, timeout): 
    conn = urllib.request.urlopen(url, timeout=timeout) 
    return conn.readall() 

# We can use a with statement to ensure threads are cleaned up promptly 
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor: 
    # Start the load operations and mark each future with its URL 
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS} 
    for future in concurrent.futures.as_completed(future_to_url): 
     url = future_to_url[future] 
     try: 
      data = future.result() 
      # do json processing here 
     except Exception as exc: 
      print('%r generated an exception: %s' % (url, exc)) 
     else: 
      print('%r page is %d bytes' % (url, len(data))) 

Można zastąpić urllib.request połączeń z requests połączeń, jeśli sobie tego życzą. Z oczywistych powodów mam tendencję do podobania się bardziej.

Interfejs API wygląda trochę tak: zrób kilka obiektów reprezentujących asynchroniczne wykonywanie funkcji. Następnie należy użyć concurrent.futures.as_completed, aby podać iterator dla instancji Future. Opuści je, gdy zostaną ukończone.

Co do twojego pytania:

Ponadto, istnieje zasada, aby dowiedzieć się optymalną liczbę wątków w zależności od liczby wniosków, czy istnieje?

Zasada kciuka, nie. To zależy od zbyt wielu rzeczy, w tym szybkości połączenia internetowego. Powiem, że to naprawdę nie zależy od liczby otrzymanych żądań, więcej od sprzętu, który używasz.

Na szczęście dość łatwo jest poprawić koder max_workers i przetestować samodzielnie. Rozpocznij od 5 lub 10 wątków, zwiększaj przyrosty o 5. Prawdopodobnie zauważysz w pewnym momencie obniżenie wydajności, a następnie zaczniesz zmniejszać, ponieważ dodatkowy narzut dodawania dodatkowych nici przewyższa marginalne wzmocnienie zwiększonej równoległości (co jest słowem) .

+0

Pozwolę sobie powiedzieć, że istnieją ograniczenia dotyczące otwartych wątków, z jakimi miałem do czynienia na naszych komputerach AWS, ale nie na moim laptopie. Kwestię tę nakreślono tutaj: http://www.alak.cc/2011/11/python-threaderror-cant-start-new.html – BenDundee

+0

@roippi zapoznałeś się z modułem wniosków-futures, którego poważałem w moim oryginalnym wpisie ? Implementuje prawie ten sam kod. –

+0

@NikolayDerkach nie, nie, ale patrząc na to .. huh! Zawija to w zasadzie w jedno wywołanie API, co jest całkiem miłe. Jedyny problem polega na tym, że jeśli jest wolny/źle działa, nie ma możliwości odwołania się do niego. Możesz łatwiej instrumentować powyższy kod, gdy coś pójdzie nie tak, na przykład. Mimo wszystko, powodzenia :) – roippi

Powiązane problemy