web-dev-qa-db-fra.com

Python requêtes avec multithreading

J'ai essayé de construire un grattoir avec une fonctionnalité multithreading depuis deux jours. D'une certaine manière, je ne pouvais toujours pas y arriver. Au début, j'ai essayé une approche multithreading régulière avec un module de threading mais ce n'était pas plus rapide que d'utiliser un seul thread. Plus tard, j'ai appris que les demandes bloquaient et que l'approche multithreading ne fonctionnait pas vraiment. J'ai donc continué mes recherches et découvert les grèves et les événements. Maintenant, je lance des tests avec gevent et ce n'est toujours pas plus rapide que d'utiliser un seul thread. Mon codage est-il incorrect?

Voici la partie pertinente de ma classe:

import gevent.monkey
from gevent.pool import Pool
import requests

gevent.monkey.patch_all()

class Test:
    def __init__(self):
        self.session = requests.Session()
        self.pool = Pool(20)
        self.urls = [...urls...]

    def fetch(self, url):

        try:
            response = self.session.get(url, headers=self.headers)
        except:
            self.logger.error('Problem: ', id, exc_info=True)

        self.doSomething(response)

    def async(self):
        for url in self.urls:
            self.pool.spawn( self.fetch, url )

        self.pool.join()

test = Test()
test.async()
19
krypt

Installez le module grequests qui fonctionne avec gevent (requests n'est pas conçu pour async):

pip install grequests

Modifiez ensuite le code en quelque chose comme ceci:

import grequests

class Test:
    def __init__(self):
        self.urls = [
            'http://www.example.com',
            'http://www.google.com', 
            'http://www.yahoo.com',
            'http://www.stackoverflow.com/',
            'http://www.reddit.com/'
        ]

    def exception(self, request, exception):
        print "Problem: {}: {}".format(request.url, exception)

    def async(self):
        results = grequests.map((grequests.get(u) for u in self.urls), exception_handler=self.exception, size=5)
        print results

test = Test()
test.async()

C'est officiellement recommandé par le projet requests:

Blocage ou non-blocage?

Avec l'adaptateur de transport par défaut en place, Requests ne fournit aucun type d'E/S non bloquant. Le Response.content la propriété sera bloquée jusqu'à ce que la réponse complète ait été téléchargée. Si vous avez besoin de plus de granularité, les fonctionnalités de streaming de la bibliothèque (voir Demandes de streaming ) vous permettent de récupérer de plus petites quantités de réponse à la fois. Cependant, ces appels seront toujours bloqués.

Si vous êtes préoccupé par l'utilisation du blocage des E/S, il existe de nombreux projets qui combinent des demandes avec l'un des cadres d'asynchronicité de Python. Deux excellents exemples sont grequests et requests-futures .

L'utilisation de cette méthode me donne une augmentation notable des performances avec 10 URL: 0.877s contre 3.852s avec votre méthode d'origine.

28
Will