Asyncio'da istekleri nasıl kullanabilirim?


128

İçinde paralel http istek görevleri yapmak istiyorum asyncio, ancak python-requestsbunun olay döngüsünü engelleyeceğini görüyorum asyncio. Aiohttp buldum, ancak bir http proxy kullanarak http isteği hizmetini sağlayamadı.

Bu yüzden, yardımıyla asenkron http istekleri yapmanın bir yolu olup olmadığını bilmek istiyorum asyncio.


1
Yalnızca istek gönderiyorsanız subprocess, kodunuzu paralel olarak kullanabilirsiniz .
WeaselFox

Bu yöntem zarif görünmüyor ……
broşürü

Artık bir asyncio bağlantı noktası istek var. github.com/rdbhost/yieldfromRequests
Rdbhost

Yanıtlar:


182

Asyncio ile istekleri (veya diğer engelleme kitaplıklarını) kullanmak için, BaseEventLoop.run_in_executor işlevini başka bir iş parçacığında bir işlevi çalıştırmak ve sonucu almak için ondan vermek üzere kullanabilirsiniz. Örneğin:

import asyncio
import requests

@asyncio.coroutine
def main():
    loop = asyncio.get_event_loop()
    future1 = loop.run_in_executor(None, requests.get, 'http://www.google.com')
    future2 = loop.run_in_executor(None, requests.get, 'http://www.google.co.uk')
    response1 = yield from future1
    response2 = yield from future2
    print(response1.text)
    print(response2.text)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Bu, her iki yanıtı da paralel olarak alacaktır.

Python 3.5 ile yeni await/ asyncsözdizimini kullanabilirsiniz:

import asyncio
import requests

async def main():
    loop = asyncio.get_event_loop()
    future1 = loop.run_in_executor(None, requests.get, 'http://www.google.com')
    future2 = loop.run_in_executor(None, requests.get, 'http://www.google.co.uk')
    response1 = await future1
    response2 = await future2
    print(response1.text)
    print(response2.text)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Daha fazlası için PEP0492'ye bakın .


5
Bunun tam olarak nasıl çalıştığını açıklayabilir misin? Bunun nasıl engellenmediğini anlamıyorum.
Scott Coates

32
@christian ama eşzamanlı olarak başka bir iş parçacığında çalışıyorsa, bu asyncio noktasını alt etmiyor mu?
Scott Coates

21
@scoarescoare Burada 'doğru yaparsanız' bölümü devreye giriyor - yürüticide çalıştırdığınız yöntem bağımsız olmalıdır ((çoğunlukla) yukarıdaki örnekte request.get gibi). Bu şekilde paylaşılan bellek, kilitleme vb. İle uğraşmak zorunda kalmazsınız ve programınızın karmaşık kısımları asyncio sayesinde hala tek iş parçacıklıdır.
christian

5
@scoarescoare Ana kullanım durumu, asyncio desteği olmayan IO kitaplıklarıyla entegrasyon içindir. Örneğin, gerçekten eski bir SOAP arayüzü ile bazı işler yapıyorum ve "en az kötü" çözüm olarak suds-jurko kütüphanesini kullanıyorum. Onu bir asyncio sunucusuyla bütünleştirmeye çalışıyorum, bu yüzden bloke edici suds çağrılarını asenkron görünecek şekilde yapmak için run_in_executor'u kullanıyorum .
Lucretiel

10
Gerçekten bu eserler ve bu yüzden eski şeyler için çok kolay olduğunu soğutmak, ancak bu kullanan bir işletim sistemi ThreadPool'da vurgulanmalı ve böylece aiohttp gibi gerçek bir asyncio odaklı lib olarak büyütmek değil mi
jsalter

78

aiohttp zaten HTTP proxy ile kullanılabilir:

import asyncio
import aiohttp


@asyncio.coroutine
def do_request():
    proxy_url = 'http://localhost:8118'  # your proxy address
    response = yield from aiohttp.request(
        'GET', 'http://google.com',
        proxy=proxy_url,
    )
    return response

loop = asyncio.get_event_loop()
loop.run_until_complete(do_request())

Konektör burada ne yapıyor?
Markus Meskanen

Proxy sunucusu üzerinden bağlantı sağlar
mindmaster

16
Bu, istekleri ayrı bir iş parçacığında kullanmaktan çok daha iyi bir çözümdür. Gerçekten eşzamansız olduğu için daha düşük ek yük ve daha düşük mem kullanımı vardır.
Thom

14
python> = 3.5 için @ asyncio.coroutine yerine "eşzamansız" ve "verim" yerine "await" yazın
James

41

Yukarıdaki cevaplar hala eski Python 3.4 stili coroutinleri kullanıyor. Python 3.5+ sürümüne sahipseniz yazacağınız şey burada.

aiohttp şimdi http proxy'yi destekliyor

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    urls = [
            'http://python.org',
            'https://google.com',
            'http://yifei.me'
        ]
    tasks = []
    async with aiohttp.ClientSession() as session:
        for url in urls:
            tasks.append(fetch(session, url))
        htmls = await asyncio.gather(*tasks)
        for html in htmls:
            print(html[:100])

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

1
daha fazla url ile detaylandırır mısın? Soru paralel http isteği ile ilgili olduğunda tek bir url'ye sahip olmanın bir anlamı yoktur.
anonim

Efsane. Teşekkür ederim! Harika çalışıyor
Adam

@ospider Bu kod, paralel olarak 100 istek kullanarak örneğin 10k URL verecek şekilde nasıl değiştirilebilir? Fikir sonraki 100. başlatılabilmesi için teslim olmamak 100 beklemek, aynı anda tüm 100 yuva kullanmaktır
Antoan Milkov

@AntoanMilkov Bu, yorum alanında cevaplanamayan farklı bir soru.
ospider

@ospider Haklısınız, işte soru: stackoverflow.com/questions/56523043/…
Antoan Milkov

11

İstekler şu anda desteklenmemektedir asynciove bu tür bir desteği sağlamaya yönelik herhangi bir plan bulunmamaktadır. Nasıl kullanılacağını bilen ( burada tartışıldığı gibi ) özel bir "Taşıma Adaptörü" uygulayabilirsiniz asyncio.

Kendimi bir süre bulursam, bu gerçekten araştırabileceğim bir şey, ama hiçbir şey için söz veremem.


Bağlantı
404'e çıkıyor

8

Pimin Konstantin Kefaloukos tarafından yazılan bir makalede zaman uyumsuz / bekleme döngüleri ve iş parçacığı iyi bir durum var Python ve asyncio ile kolay paralel HTTP istekleri :

Toplam tamamlanma süresini en aza indirmek için, iş parçacığı havuzunun boyutunu yapmamız gereken istek sayısıyla eşleşecek şekilde artırabiliriz. Neyse ki, ileride göreceğimiz gibi bunu yapmak çok kolay. Aşağıdaki kod listesi, yirmi çalışan iş parçacığından oluşan bir iş parçacığı havuzuyla yirmi eşzamansız HTTP isteğinin nasıl yapılacağına dair bir örnektir:

# Example 3: asynchronous requests with larger thread pool
import asyncio
import concurrent.futures
import requests

async def main():

    with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor:

        loop = asyncio.get_event_loop()
        futures = [
            loop.run_in_executor(
                executor, 
                requests.get, 
                'http://example.org/'
            )
            for i in range(20)
        ]
        for response in await asyncio.gather(*futures):
            pass


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

2
bununla ilgili sorun şu ki, 20 yürütücünün yığınlarıyla 10000 istek çalıştırmam gerekirse, sonraki 20 ile başlamak için 20 yürütücünün tamamının bitmesini beklemem gerekir, değil mi? Yapamam for i in range(10000)çünkü bir istek başarısız olabilir veya zaman aşımı olabilir, değil mi?
Sanandrea

1
Sadece ThreadPoolExecutor'u kullanarak aynısını yapabiliyorsanız, neden asyncio'ya ihtiyacınız olduğunu açıklayabilir misiniz?
Asaf Pinhassi

@lya Rusin Neye dayanarak max_workers sayısını belirliyor muyuz? CPU sayısı ve iş parçacığı ile bir ilgisi var mı?
alt-f4
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.