मैं asyncio में अनुरोधों का उपयोग कैसे कर सकता हूं?


127

मैं समानांतर http अनुरोध कार्यों को करना चाहता हूं asyncio, लेकिन मुझे लगता है कि python-requestsइवेंट लूप को ब्लॉक कर देगा asyncio। मैंने aiohttp पाया है लेकिन यह http प्रॉक्सी का उपयोग करके http अनुरोध की सेवा प्रदान नहीं कर सका।

इसलिए मैं जानना चाहता हूं कि क्या अतुल्यकालिक http अनुरोधों की मदद से करने का कोई तरीका है asyncio


1
यदि आप केवल अनुरोध भेज रहे हैं तो आप subprocessअपने कोड को समानांतर करने के लिए उपयोग कर सकते हैं ।
वीज़ेलफ़ॉक्स

यह विधि सुरुचिपूर्ण नहीं है ……
फ्लायर

अब अनुरोधों का एक एसिंको पोर्ट है। github.com/rdbhost/yieldfromRequests
Rdbhost

जवाबों:


181

Asyncio के साथ अनुरोधों (या किसी अन्य ब्लॉकिंग लाइब्रेरी) का उपयोग करने के लिए, आप परिणाम प्राप्त करने के लिए BaseEventLoop.run_in_executor का उपयोग किसी अन्य थ्रेड में चलाने और इससे उपज प्राप्त करने के लिए कर सकते हैं। उदाहरण के लिए:

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

इससे समानांतर रूप से दोनों प्रतिक्रियाएं मिलेंगी।

अजगर 3.5 के साथ आप नए await/ asyncवाक्यविन्यास का उपयोग कर सकते हैं :

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

अधिक के लिए PEP0492 देखें ।


5
क्या आप बता सकते हैं कि यह वास्तव में कैसे काम करता है? मुझे समझ नहीं आता कि यह कैसे अवरुद्ध नहीं होता है।
स्कॉट कोट

32
@ क्रिश्चियन लेकिन अगर इसके दूसरे धागे में समवर्ती चल रहा है, तो क्या यह एसिंको की बात को नहीं हरा रहा है?
स्कॉट

21
@scoarescoare यह वह जगह है जहाँ 'यदि आप इसे सही करते हैं' भाग आता है - विधि जिसे आप निष्पादक में चलाते हैं, वह आत्म-सम्‍मिलित होना चाहिए ((अधिकतर) उपरोक्त उदाहरण में request.get की तरह)। इस तरह से आपको साझा मेमोरी, लॉकिंग आदि से जूझना नहीं पड़ता है, और आपके प्रोग्राम के जटिल हिस्से अभी भी सिंगल थ्रेडेड हैं।
क्रिश्चियन

5
@scoarescoare मुख्य उपयोग मामला IO पुस्तकालयों के साथ एकीकृत करने के लिए है जिनके पास एसिंको के लिए समर्थन नहीं है। उदाहरण के लिए, मैं वास्तव में प्राचीन SOAP इंटरफ़ेस के साथ कुछ काम कर रहा हूं, और मैं suds-jurko लाइब्रेरी का उपयोग "कम से कम खराब" समाधान के रूप में कर रहा हूं। मैं इसे एक एसिंको सर्वर के साथ एकीकृत करने का प्रयास कर रहा हूं, इसलिए मैं अवरुद्ध करने वाले एसडीएस कॉल को इस तरह से चलाने के लिए run_in_executor का उपयोग कर रहा हूं जो अतुल्यकालिक दिखता है
ल्यूसट्रिएल

10
वास्तव में अच्छा है कि यह काम करता है और इसलिए यह विरासत की चीजों के लिए इतना आसान है, लेकिन इस बात पर जोर दिया जाना चाहिए कि यह ओएस थ्रेडपूल का उपयोग करता है और इसलिए यह एक सच्चे एसिंकोयो उन्मुख काम के रूप में स्केल नहीं करता है जैसे कि aiohttp करता है
jsalter

78

पहले से ही HTTP प्रॉक्सी के साथ aiohttp का उपयोग किया जा सकता है:

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

कनेक्टर यहाँ क्या करता है?
मार्कस मेस्कैनन

यह प्रॉक्सी सर्वर के माध्यम से एक कनेक्शन प्रदान करता है
माइंडमास्टर

16
यह एक बेहतर समाधान है तो एक अलग थ्रेड में अनुरोधों का उपयोग करने के लिए। चूँकि यह वास्तव में async है इसलिए इसमें ओवरहेड और कम मेम उपयोग है।
Thom

14
अजगर के लिए> = 3.5 "async" के साथ @ asyncio.coroutine बदलें और "इंतजार" के साथ "से उपज"
जेम्स

41

ऊपर दिए गए उत्तर अभी भी पुराने पायथन 3.4 शैली के कोरआउट्स का उपयोग कर रहे हैं। यदि आप पायथन 3.5+ प्राप्त करते हैं तो आप यहाँ क्या लिखेंगे।

aiohttp अब HTTP प्रॉक्सी का समर्थन करता है

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
क्या आप अधिक यूआरएल के साथ विस्तृत कर सकते हैं? जब समानांतर http अनुरोध के बारे में सवाल होता है तो इसका केवल एक ही यूआरएल होने का कोई मतलब नहीं है।
अनाम

लीजेंड। धन्यवाद! महान काम करता है
एडम

@ospider समानांतर में 100 अनुरोधों का उपयोग करके 10k URL कहने के लिए इस कोड को कैसे संशोधित किया जा सकता है? अगले 100 को शुरू करने के लिए सभी 100 स्लॉट्स को एक साथ उपयोग करने के लिए, 100 के लिए इंतजार नहीं किया जाना चाहिए।
एंटोन मिल्कोव

@AntoanMilkov यह एक अलग सवाल है जिसका जवाब टिप्पणी क्षेत्र में नहीं दिया जा सकता है।
ospider

@ospider आप सही हैं, यहाँ सवाल है: stackoverflow.com/questions/56523043/…
Antoan Milkov

11

अनुरोध वर्तमान में समर्थन नहीं करते हैं asyncioऔर ऐसे समर्थन प्रदान करने की कोई योजना नहीं है। यह संभावना है कि आप एक कस्टम "ट्रांसपोर्ट एडॉप्टर" लागू कर सकते हैं (जैसा कि यहां चर्चा की गई है ) जो जानता है कि कैसे उपयोग करना है asyncio

अगर मैं खुद को कुछ समय के साथ पाता हूं तो यह कुछ ऐसा है जिसे मैं वास्तव में देख सकता हूं, लेकिन मैं कुछ भी वादा नहीं कर सकता।


लिंक 404 की ओर जाता है।
कोडबाइकर

8

पिस्टन कोन्स्टेंटिन केफालोकोस द्वारा एक लेख में एसिंक्स / वेट लूप्स और थ्रेडिंग का एक अच्छा मामला है। पायथन और एसिंको के साथ आसान समानांतर HTTP अनुरोध :

कुल पूर्णता समय को कम करने के लिए, हमें अपने द्वारा किए जाने वाले अनुरोधों की संख्या से मिलान करने के लिए थ्रेड पूल का आकार बढ़ा सकते हैं। सौभाग्य से, यह करना आसान है जैसा कि हम अगले देखेंगे। नीचे दी गई कोड सूची बीस वर्कर थ्रेड्स के थ्रेड पूल के साथ बीस अतुल्यकालिक HTTP अनुरोध बनाने का एक उदाहरण है:

# 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
इसके साथ समस्या यह है कि अगर मुझे 20 निष्पादकों के चांस के साथ 10000 अनुरोधों को चलाने की आवश्यकता है, तो मुझे अगले 20 के साथ शुरू करने के लिए सभी 20 निष्पादकों के लिए इंतजार करना होगा, है ना? मैं नहीं कर for i in range(10000)सकता क्योंकि एक अनुरोध विफल हो सकता है या समय समाप्त हो सकता है, है ना?
सानंद्रिया

1
क्या आप pls बता सकते हैं कि जब आपको थ्रेडपूल एक्सक्यूसॉर का उपयोग करके आप ऐसा कर सकते हैं तो आपको एसिंको की आवश्यकता क्यों है?
आसफ पिंहासी

@lya रुसिन किस के आधार पर, हम max_workers की संख्या निर्धारित करते हैं? क्या सीपीयू और थ्रेड्स की संख्या के साथ यह करना है?
alt-f4
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.