Bagaimana saya bisa menggunakan permintaan di asyncio?

126

Saya ingin melakukan tugas permintaan http paralel asyncio, tetapi saya menemukan bahwa itu python-requestsakan memblokir loop peristiwa asyncio. Saya telah menemukan aiohttp tetapi tidak dapat menyediakan layanan permintaan http menggunakan proxy http.

Jadi saya ingin tahu apakah ada cara untuk melakukan permintaan http asinkron dengan bantuan asyncio.

pamflet
sumber
1
Jika Anda hanya mengirim permintaan, Anda bisa menggunakan subprocesskode Anda secara paralel.
WeaselFox
Metode ini sepertinya tidak elegan ……
flyer
Sekarang ada port permintaan asyncio. github.com/rdbhost/yieldfromRequests
Rdbhost

Jawaban:

181

Untuk menggunakan permintaan (atau pustaka pemblokiran lainnya) dengan asyncio, Anda bisa menggunakan BaseEventLoop.run_in_executor untuk menjalankan fungsi di utas lain dan menghasilkan darinya untuk mendapatkan hasilnya. Sebagai contoh:

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())

Ini akan mendapatkan kedua respons secara paralel.

Dengan python 3.5 Anda dapat menggunakan await/ asyncsintaks baru:

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())

Lihat PEP0492 untuk lebih lanjut.

Kristen
sumber
5
Bisakah Anda menjelaskan bagaimana tepatnya ini bekerja? Saya tidak mengerti bagaimana ini tidak menghalangi.
Scott Coates
32
@ Kristen tetapi jika itu berjalan bersamaan di utas lain, bukankah itu mengalahkan titik asyncio?
Scott Coates
21
@scoarescoare Di situlah bagian 'jika Anda melakukannya dengan benar' - metode yang Anda jalankan dalam pelaksana harus mandiri ((kebanyakan) seperti requests.get dalam contoh di atas). Dengan begitu Anda tidak harus berurusan dengan memori bersama, penguncian, dll., Dan bagian kompleks dari program Anda masih berulir tunggal berkat asyncio.
christian
5
@scoarescoare Kasus penggunaan utama adalah untuk berintegrasi dengan pustaka IO yang tidak memiliki dukungan untuk asyncio. Sebagai contoh, saya melakukan beberapa pekerjaan dengan antarmuka SOAP yang benar-benar kuno, dan saya menggunakan perpustakaan suds-jurko sebagai solusi "paling buruk". Saya mencoba mengintegrasikannya dengan server asyncio, jadi saya menggunakan run_in_executor untuk melakukan pemblokiran panggilan suds dengan cara yang terlihat tidak sinkron.
Lucretiel
10
Sangat keren bahwa ini bekerja dan sangat mudah untuk hal-hal lama, tetapi harus ditekankan bahwa ini menggunakan threadpool OS dan jadi tidak ditingkatkan sebagai lib berorientasi asyncio yang benar seperti yang dilakukan
aiohttp
78

aiohttp sudah bisa digunakan dengan proxy HTTP:

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())
pemimpin pikiran
sumber
Apa yang dilakukan konektor di sini?
Markus Meskanen
Ini menyediakan koneksi melalui server proxy
mindmaster
16
Ini adalah solusi yang jauh lebih baik daripada menggunakan permintaan di utas terpisah. Karena benar-benar async, ia memiliki overhead yang lebih rendah dan penggunaan mem yang lebih rendah.
Thom
14
untuk python> = 3.5 ganti @ asyncio.coroutine dengan "async" dan "hasil dari" dengan "menunggu"
James
40

Jawaban di atas masih menggunakan coroutine style Python 3.4 lama. Inilah yang akan Anda tulis jika Anda mendapat Python 3.5+.

aiohttp mendukung proksi http sekarang

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())
ospider
sumber
1
Bisakah Anda menguraikan lebih banyak url? Tidak masuk akal memiliki hanya satu url ketika pertanyaannya adalah tentang permintaan http paralel.
Anonim
Legenda. Terima kasih! Bekerja dengan baik
Adam
@ospider Bagaimana kode ini dapat dimodifikasi untuk mengirimkan katakanlah 10k URL menggunakan 100 permintaan secara paralel? Idenya adalah untuk menggunakan semua 100 slot secara bersamaan, tidak menunggu 100 untuk dikirimkan untuk memulai 100 berikutnya.
Antoan Milkov
@AntoanMilkov Itu pertanyaan berbeda yang tidak bisa dijawab di area komentar.
ospider
@ospider Anda benar, ini pertanyaannya: stackoverflow.com/questions/56523043/…
Antoan Milkov
11

Permintaan saat ini tidak mendukung asynciodan tidak ada rencana untuk memberikan dukungan tersebut. Kemungkinan Anda dapat menerapkan "Transport Adapter" khusus (seperti yang dibahas di sini ) yang tahu cara menggunakannya asyncio.

Jika saya menemukan diri saya dengan beberapa waktu itu adalah sesuatu yang saya mungkin benar-benar melihat ke dalam, tetapi saya tidak bisa menjanjikan apa pun.

Lukasa
sumber
Tautan mengarah ke 404.
CodeBiker
8

Ada beberapa kasus async / menunggu loop dan threading dalam artikel oleh Pimin Konstantin Kefaloukos Permintaan HTTP paralel yang mudah dengan Python dan asyncio :

Untuk meminimalkan total waktu penyelesaian, kami dapat menambah ukuran kumpulan utas agar sesuai dengan jumlah permintaan yang harus kami buat. Untungnya, ini mudah dilakukan karena akan kita lihat selanjutnya. Daftar kode di bawah ini adalah contoh cara membuat dua puluh permintaan HTTP asinkron dengan kumpulan utas dari dua utas pekerja:

# 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())
Ilya Rusin
sumber
2
masalah dengan ini adalah bahwa jika saya perlu menjalankan 10.000 permintaan dengan potongan 20 eksekutor, saya harus menunggu semua 20 eksekutor untuk menyelesaikan untuk memulai dengan 20 berikutnya, kan? Saya tidak dapat melakukannya for i in range(10000)karena satu permintaan mungkin gagal atau kehabisan waktu, bukan?
Sanandrea
1
Bisakah Anda jelaskan mengapa Anda perlu asyncio ketika Anda dapat melakukan hal yang sama hanya menggunakan ThreadPoolExecutor?
Asaf Pinhassi
@lya Rusin Berdasarkan apa, apakah kita menetapkan jumlah pekerja maks_? Apakah ada hubungannya dengan jumlah CPU dan utas?
alt-f4