पायथन में 100,000 HTTP अनुरोध भेजने का सबसे तेज़ तरीका क्या है?


287

मैं एक फाइल खोल रहा हूं जिसमें 100,000 URL हैं। मुझे प्रत्येक URL पर एक HTTP अनुरोध भेजने और स्थिति कोड प्रिंट करने की आवश्यकता है। मैं पाइथन 2.6 का उपयोग कर रहा हूं, और अब तक पाइथन के कई भ्रामक तरीकों को देखा गया है जो थ्रेडिंग / कॉन्सिरेन्सी है। मैंने अजगर सम्मोहन पुस्तकालय को भी देखा है , लेकिन इस कार्यक्रम को सही ढंग से कैसे लिखा जाए, यह पता नहीं लगा सकता। क्या किसी को भी इसी तरह की समस्या आई है? मुझे लगता है कि आम तौर पर मुझे यह जानने की जरूरत है कि पायथन में हजारों कार्यों को जितनी तेजी से संभव हो सके - मुझे लगता है कि इसका मतलब 'समवर्ती' है।


47
सुनिश्चित करें कि आप केवल HEAD अनुरोध करते हैं (ताकि आप संपूर्ण दस्तावेज़ डाउनलोड न करें)। देखें: stackoverflow.com/questions/107405/…
टार्ने कल्मन

5
बहुत बढ़िया बात, कलमी। अगर सभी इगोर चाहते हैं अनुरोध की स्थिति, इन 100K अनुरोध बहुत, बहुत, बहुत जल्दी जाना जाएगा। बहुत जल्दी।
एडम क्रॉसलैंड

1
इसके लिए आपको थ्रेड्स की आवश्यकता नहीं है; सबसे कुशल तरीका मुड़ की तरह एक अतुल्यकालिक पुस्तकालय का उपयोग करने की संभावना है।
jemfinch

3
यहाँ पर जियोवेंट, ट्विस्टेड और एसिनसियो-आधारित कोड उदाहरण (1000000 अनुरोधों पर परीक्षण किए गए)
jfs

4
@ TarnayKálmán के लिए यह संभव है requests.getऔर requests.head(यानी एक पेज अनुरोध बनाम एक सिर अनुरोध) विभिन्न स्थिति कोड वापस करने के लिए, इसलिए यह सबसे अच्छी सलाह नहीं है
एलेक्सग

जवाबों:


200

निराधार समाधान:

from urlparse import urlparse
from threading import Thread
import httplib, sys
from Queue import Queue

concurrent = 200

def doWork():
    while True:
        url = q.get()
        status, url = getStatus(url)
        doSomethingWithResult(status, url)
        q.task_done()

def getStatus(ourl):
    try:
        url = urlparse(ourl)
        conn = httplib.HTTPConnection(url.netloc)   
        conn.request("HEAD", url.path)
        res = conn.getresponse()
        return res.status, ourl
    except:
        return "error", ourl

def doSomethingWithResult(status, url):
    print status, url

q = Queue(concurrent * 2)
for i in range(concurrent):
    t = Thread(target=doWork)
    t.daemon = True
    t.start()
try:
    for url in open('urllist.txt'):
        q.put(url.strip())
    q.join()
except KeyboardInterrupt:
    sys.exit(1)

यह एक मुड़ समाधान की तुलना में थोड़ा तेज है और कम सीपीयू का उपयोग करता है।


10
@ कलमी, आप क्यू को क्यों सेट करते हैं concurrent*2?
मार्सेल विल्सन

8
कनेक्शन बंद करने के लिए मत भूलना conn.close()। कई http कनेक्शन खोलने से आपकी स्क्रिप्ट कुछ बिंदु पर रुक सकती है और मेमोरी को खा जाती है।
आमिर अदनान

4
@ क्यों, Queueमॉड्यूल का नाम बदलकर queueअजगर 3 कर दिया गया है। यह पायथन 2 कोड है।
तरणाय कालमन

3
यदि कनेक्शन जारी रखकर आप हर बार SAME सर्वर के साथ बात करना चाहते हैं तो आप कितनी तेजी से जा सकते हैं? यह भी धागे के पार किया जा सकता है, या प्रति धागे एक लगातार कनेक्शन के साथ?
मादुरंत

2
@mptevsion, यदि आप CPython का उपयोग कर रहे हैं, तो आप (उदाहरण के लिए) "my_global_list.append ((स्थिति, url))" के साथ "प्रिंट स्थिति, url" को बदल सकते हैं। (अधिकांश संचालन) सूची GIL के कारण CPython (और कुछ अन्य अजगर कार्यान्वयन) में स्पष्ट रूप से धागा-सुरक्षित हैं, इसलिए ऐसा करना सुरक्षित है।
तर्ने कल्लन

54

बवंडर अतुल्यकालिक नेटवर्किंग लाइब्रेरी का उपयोग कर एक समाधान

from tornado import ioloop, httpclient

i = 0

def handle_request(response):
    print(response.code)
    global i
    i -= 1
    if i == 0:
        ioloop.IOLoop.instance().stop()

http_client = httpclient.AsyncHTTPClient()
for url in open('urls.txt'):
    i += 1
    http_client.fetch(url.strip(), handle_request, method='HEAD')
ioloop.IOLoop.instance().start()

7
यह कोड गैर-अवरोधक नेटवर्क I / O का उपयोग कर रहा है और इसमें कोई प्रतिबंध नहीं है। यह हजारों खुले कनेक्शनों के दसियों पैमाने पर हो सकता है। यह एक ही धागे में चलेगा, लेकिन तेजी से फैलने वाला एक उपाय होगा। चेकआउट नॉन-ब्लॉकिंग I / O en.wikipedia.org/wiki/Asynchronous_I/O
mher

1
क्या आप बता सकते हैं कि वैश्विक i चर के साथ यहां क्या हो रहा है? किसी प्रकार की त्रुटि जाँच?
LittleBobbyTables

4
यह निर्धारित करने के लिए एक काउंटर है कि `` आयोलोप` से बाहर निकलने के लिए - तो जब आप कर रहे हैं।
माइकल डॉर्नर

1
@AndrewScottEvans ने यह मान लिया कि आप अजगर 2.7 और परदे के पीछे का उपयोग कर रहे हैं
Dejell

5
@ गुय अवराम सौभाग्य को आपके ddos ​​प्लान पर मदद मिल रही है।
वाल्टर

51

2010 से जब यह पोस्ट किया गया था तब चीजें काफी बदल गई हैं और मैंने अन्य सभी उत्तरों की कोशिश नहीं की है, लेकिन मैंने कुछ कोशिश की है, और मुझे यह python3.6 का उपयोग करके मेरे लिए सबसे अच्छा काम करने के लिए मिला।

मैं AWS पर प्रति सेकंड लगभग 150 अद्वितीय डोमेन लाने में सक्षम था।

import pandas as pd
import concurrent.futures
import requests
import time

out = []
CONNECTIONS = 100
TIMEOUT = 5

tlds = open('../data/sample_1k.txt').read().splitlines()
urls = ['http://{}'.format(x) for x in tlds[1:]]

def load_url(url, timeout):
    ans = requests.head(url, timeout=timeout)
    return ans.status_code

with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor:
    future_to_url = (executor.submit(load_url, url, TIMEOUT) for url in urls)
    time1 = time.time()
    for future in concurrent.futures.as_completed(future_to_url):
        try:
            data = future.result()
        except Exception as exc:
            data = str(type(exc))
        finally:
            out.append(data)

            print(str(len(out)),end="\r")

    time2 = time.time()

print(f'Took {time2-time1:.2f} s')
print(pd.Series(out).value_counts())

1
मैं केवल इसलिए पूछ रहा हूं क्योंकि मुझे नहीं पता, लेकिन क्या इस फ्यूचर सामान को एसिंक्स / वेट से बदला जा सकता है?
टैंकरस्मैश

1
यह हो सकता है, लेकिन मैंने ऊपर काम करने के लिए बेहतर पाया है। आप aiohttp का उपयोग कर सकते हैं लेकिन इसके मानक लिबास का हिस्सा नहीं है और यह काफी बदल रहा है। यह काम करता है, लेकिन मैं सिर्फ यह भी काम करने के लिए नहीं मिला है। जब मैं इसका उपयोग करता हूं तो मुझे उच्च त्रुटि दर मिलती है और मेरे जीवन के लिए मुझे यह काम करने के लिए नहीं मिल सकता है, हालांकि समवर्ती वायदा हालांकि सिद्धांत में ऐसा लगता है कि इसे बेहतर काम करना चाहिए, देखें: stackoverflow.com/questions/45800857/… यदि आप इसे अच्छी तरह से काम करने के लिए प्राप्त करते हैं तो कृपया अपना उत्तर पोस्ट करें ताकि मैं इसका परीक्षण कर सकूं।
ग्लेन थॉम्पसन

1
यह एक नाइटपिक है, लेकिन मुझे लगता है कि time1 = time.time()लूप के लिए सबसे ऊपर और time2 = time.time()राइट के लिए लूप के बाद इसे रखना काफी क्लीनर है ।
मैट एम।

मैंने आपके स्निपेट का परीक्षण किया, किसी तरह यह दो बार निष्पादित होता है। क्या मुझसे कुछ गलत हो रही है? या यह दो बार चलाने के लिए है? यदि इसका बाद का मामला है, तो क्या आप मुझे यह समझने में मदद कर सकते हैं कि यह दो बार कैसे ट्रिगर होता है?
रॉनी

1
इसे दो बार नहीं चलाना चाहिए। यकीन नहीं होता कि आप ऐसा क्यों देख रहे हैं।
ग्लेन थॉम्पसन

40

धागे बिल्कुल जवाब नहीं हैं। वे प्रक्रिया और कर्नेल अड़चन दोनों प्रदान करेंगे, साथ ही साथ थ्रूपुट सीमाएं जो स्वीकार्य नहीं हैं यदि समग्र लक्ष्य "सबसे तेज गति" है।

थोड़ा सा twistedऔर इसके अतुल्यकालिक HTTPग्राहक आपको बहुत बेहतर परिणाम देंगे।


ironfroggy: मैं आपकी भावनाओं की ओर झुक रहा हूं। मैंने धागे और कतारों (स्वचालित म्यूटेक्स के लिए) के साथ अपने समाधान को लागू करने की कोशिश की, लेकिन क्या आप कल्पना कर सकते हैं कि 100,000 चीजों के साथ कतार को आबाद करने में कितना समय लगता है ?? मैं अभी भी इस धागे पर सभी के विभिन्न विकल्पों और सुझावों के साथ खेल रहा हूं, और शायद ट्विस्ट एक अच्छा समाधान होगा।
इगोरगानापोलस्की

2
आप 100k चीजों के साथ एक कतार में आबादी से बच सकते हैं। बस अपने इनपुट से एक समय में आइटम को संसाधित करें, फिर प्रत्येक आइटम के लिए अनुरोध को संसाधित करने के लिए एक थ्रेड लॉन्च करें। (मैं नीचे का वर्णन के रूप में, HTTP अनुरोध धागे शुरू करने के लिए जब अपने धागा गिनती कुछ सीमा से नीचे है एक लांचर धागा का उपयोग धागे प्रतिक्रिया, या एक सूची संलग्न tuples के लिए एक dict मानचित्रण URL में बाहर परिणाम बारे में सुनिश्चित करें।।)
एरिक गैरीसन

ironfroggy: इसके अलावा, मैं इस बारे में उत्सुक हूं कि आपने पायथन थ्रेड्स का उपयोग करके क्या अड़चनें पाई हैं? और पायथन थ्रेड्स ओएस कर्नेल के साथ कैसे बातचीत करते हैं?
एरिक गैरीसन

सुनिश्चित करें कि आपने एपोल रिएक्टर स्थापित किया है; अन्यथा आप चुनिंदा / पोल का उपयोग करेंगे, और यह बहुत धीमा होगा। इसके अलावा, यदि आप वास्तव में एक साथ 100,000 कनेक्शन खोलने की कोशिश करने जा रहे हैं (यह मानते हुए कि आपका प्रोग्राम उस तरह से लिखा गया है, और URL अलग-अलग सर्वरों पर हैं), तो आपको अपने ओएस को ट्यून करने की आवश्यकता होगी ताकि आप रन आउट न हों फाइल डिस्क्रिप्टर, पंचांग पोर्ट, आदि (यह सिर्फ यह सुनिश्चित करना आसान है कि आपके पास इससे अधिक नहीं है, कहते हैं, एक बार में 10,000 बकाया कनेक्शन)।
मार्क नॉटिंघम

erikg: आपने एक महान विचार की सिफारिश की थी। हालांकि, 200 थ्रेड्स के साथ मैं प्राप्त करने में सक्षम सबसे अच्छा परिणाम लगभग था। 6 मिनट। मुझे यकीन है कि कम समय में इसे पूरा करने के तरीके हैं ... मार्क एन: यदि ट्विस्टेड वह तरीका है जो मैं जाने का फैसला करता हूं, तो एपोल रिएक्टर निश्चित रूप से उपयोगी है। हालाँकि, अगर मेरी स्क्रिप्ट कई मशीनों से चलाई जाएगी, तो क्या EACH मशीन पर ट्विस्टेड की स्थापना की आवश्यकता नहीं होगी? मुझे नहीं पता कि क्या मैं अपने बॉस को उस रास्ते पर जाने के लिए मना सकता हूं ...
इगोरगानापोलस्की

21

मुझे पता है कि यह एक पुराना प्रश्न है, लेकिन पायथन 3.7 में आप इसका उपयोग कर सकते हैं asyncioऔर aiohttp

import asyncio
import aiohttp
from aiohttp import ClientSession, ClientConnectorError

async def fetch_html(url: str, session: ClientSession, **kwargs) -> tuple:
    try:
        resp = await session.request(method="GET", url=url, **kwargs)
    except ClientConnectorError:
        return (url, 404)
    return (url, resp.status)

async def make_requests(urls: set, **kwargs) -> None:
    async with ClientSession() as session:
        tasks = []
        for url in urls:
            tasks.append(
                fetch_html(url=url, session=session, **kwargs)
            )
        results = await asyncio.gather(*tasks)

    for result in results:
        print(f'{result[1]} - {str(result[0])}')

if __name__ == "__main__":
    import pathlib
    import sys

    assert sys.version_info >= (3, 7), "Script requires Python 3.7+."
    here = pathlib.Path(__file__).parent

    with open(here.joinpath("urls.txt")) as infile:
        urls = set(map(str.strip, infile))

    asyncio.run(make_requests(urls=urls))

आप इसके बारे में अधिक पढ़ सकते हैं और यहां एक उदाहरण देख सकते हैं ।


क्या यह C # async / wait और Kotlin Coroutines के समान है?
इगोरगानापोलस्की

@IgorGanapolsky, हाँ, यह C # async / प्रतीक्षा के समान है। मैं कोटलिन कॉरआउट्स से परिचित नहीं हूं।
मेरियस स्टेंसनू

@sandyp, मुझे यकीन नहीं है कि अगर यह काम करता है, लेकिन अगर आप कोशिश करना चाहते हैं तो आपको aiohttp के लिए UnixConnector का उपयोग करना होगा। यहां अधिक पढ़ें: docs.aiohttp.org/en/stable/client_reference.html#connectors
मारियस स्टेंसनू

धन्यवाद @ MariusStănescu यह वही है जो मैंने इस्तेमाल किया।
संदीप

+1 asyncio.gather (* कार्य) दिखाने के लिए। यहाँ एक ऐसा स्निपेट है जिसे मैंने इस्तेमाल किया है: urls= [fetch(construct_fetch_url(u),idx) for idx, u in enumerate(some_URI_list)] results = await asyncio.gather(*urls)
अश्विनी कुमार

19

Grequests का उपयोग करें , यह अनुरोधों का संयोजन है + Gevent मॉड्यूल।

GRequests आपको आसानी से Asyncronous HTTP अनुरोध करने के लिए Gevent के साथ अनुरोधों का उपयोग करने की अनुमति देता है।

उपयोग सरल है:

import grequests

urls = [
   'http://www.heroku.com',
   'http://tablib.org',
   'http://httpbin.org',
   'http://python-requests.org',
   'http://kennethreitz.com'
]

असंतुलित अनुरोधों का एक सेट बनाएँ:

>>> rs = (grequests.get(u) for u in urls)

उन्हें एक ही समय में भेजें:

>>> grequests.map(rs)
[<Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>, <Response [200]>]

7
gevent

14
grequests सामान्य अनुरोधों का हिस्सा नहीं है और
Thom

8

इस समस्या को हल करने के लिए एक अच्छा तरीका यह है कि पहले एक परिणाम प्राप्त करने के लिए आवश्यक कोड लिखें, फिर एप्लिकेशन को समानांतर करने के लिए थ्रेडिंग कोड को शामिल करें।

एक आदर्श दुनिया में इसका मतलब बस एक साथ 100,000 धागे शुरू करना होगा, जो बाद में प्रसंस्करण के लिए एक शब्दकोश या सूची में अपने परिणामों का उत्पादन करता है, लेकिन व्यवहार में आप कितने समानांतर HTTP अनुरोधों को इस फैशन में जारी कर सकते हैं, इसमें सीमित हैं। स्थानीय रूप से, आपके पास कितने सॉकेट हैं जिन्हें आप समवर्ती रूप से खोल सकते हैं, आपके पायथन दुभाषिया को निष्पादन के कितने धागे अनुमति देंगे। दूरस्थ रूप से, आप एक साथ कनेक्शन की संख्या में सीमित हो सकते हैं यदि सभी अनुरोध एक सर्वर या कई के खिलाफ हैं। इन सीमाओं को शायद इस बात की आवश्यकता होगी कि आप स्क्रिप्ट को इस तरह से लिखें जैसे कि किसी एक समय में केवल URL के एक छोटे से हिस्से को प्रदूषित करें (100, जैसा कि एक अन्य पोस्टर में उल्लेख किया गया है, संभवतः एक सभ्य थ्रेड पूल आकार है, हालांकि आप पा सकते हैं कि आप सफलतापूर्वक कई और तैनात कर सकते हैं)।

उपरोक्त समस्या को हल करने के लिए आप इस डिज़ाइन पैटर्न का अनुसरण कर सकते हैं:

  1. एक थ्रेड प्रारंभ करें जो वर्तमान में चल रहे थ्रेड्स की संख्या तक नए थ्रेड लॉन्च करता है (आप थ्रेडिंग के माध्यम से उन्हें ट्रैक कर सकते हैं। सक्रिय_काउंट) (या थ्रेड ऑब्जेक्ट्स को डेटा संरचना में धकेलकर)> = = आपकी अधिकतम संख्या एक साथ अनुरोध (100) , तो कम समय के लिए सोता है। जब प्रक्रिया के लिए अधिक URL नहीं हैं, तो यह थ्रेड समाप्त हो जाना चाहिए। इस प्रकार, धागा जागते रहेंगे, नए धागे लॉन्च करेंगे, और जब तक आपका काम पूरा नहीं हो जाता, तब तक सोते रहेंगे।
  2. अनुरोध थ्रेड बाद के पुनः प्राप्ति और आउटपुट के लिए कुछ डेटा संरचना में अपने परिणाम संग्रहीत करते हैं। यदि आप जिस संरचना में परिणाम जमा कर रहे हैं, वह एक listया dictसीपीथॉन में है, तो आप ताले के बिना अपने थ्रेड्स से अद्वितीय वस्तुओं को सुरक्षित रूप से जोड़ सकते हैं या सम्मिलित कर सकते हैं , लेकिन यदि आप किसी फ़ाइल में लिखते हैं या अधिक जटिल क्रॉस-थ्रेड डेटा इंटरैक्शन की आवश्यकता होती है, तो आपको एक का उपयोग करना चाहिए इस राज्य को भ्रष्टाचार से बचाने के लिए आपसी बहिष्कार ताला

मेरा सुझाव है कि आप थ्रेडिंग मॉड्यूल का उपयोग करें । आप इसे थ्रेडिंग लॉन्च करने और ट्रैक करने के लिए उपयोग कर सकते हैं। पायथन के थ्रेडिंग समर्थन नंगे हैं, लेकिन आपकी समस्या का वर्णन बताता है कि यह आपकी आवश्यकताओं के लिए पूरी तरह से पर्याप्त है।

अंत में, यदि आप Python में लिखे गए समानांतर नेटवर्क अनुप्रयोग का एक बहुत ही सीधा आवेदन देखना चाहते हैं, तो ssh.py की जाँच करें । यह एक छोटा पुस्तकालय है जो कई एसएसएच कनेक्शनों को समानांतर करने के लिए पायथन थ्रेडिंग का उपयोग करता है। डिजाइन आपकी आवश्यकताओं के काफी करीब है कि आप इसे एक अच्छा संसाधन मान सकते हैं।


1
erikg: अपने समीकरण में एक कतार में फेंकना उचित होगा (आपसी-बहिष्कार लॉकिंग के लिए)? मुझे संदेह है कि पायथन का GIL हजारों धागों से खेलने की ओर अग्रसर नहीं है।
इगोरगानापोलस्की

बहुत सारे थ्रेड्स की पीढ़ी को रोकने के लिए आपको पारस्परिक-बहिष्करण लॉकिंग की आवश्यकता क्यों है? मुझे संदेह है कि मुझे पद की गलतफहमी है। आप थ्रेड कतार में चल रहे थ्रेड्स को ट्रैक कर सकते हैं, उन्हें पूरा करने पर उन्हें हटा सकते हैं और उक्त थ्रेड सीमा तक अधिक जोड़ सकते हैं। लेकिन एक साधारण मामले में जैसे प्रश्न में आप केवल मौजूदा पायथन प्रक्रिया में सक्रिय थ्रेड्स की संख्या देख सकते हैं, प्रतीक्षा करें जब तक कि यह एक सीमा से नीचे न हो जाए, और वर्णित रूप में थ्रेश तक अधिक थ्रेड लॉन्च करें। मुझे लगता है कि आप इसे एक अंतर्निहित ताला मान सकते हैं, लेकिन किसी भी स्पष्ट ताले की आवश्यकता नहीं है।
एरिक गैरीसन

erikg: कई धागे राज्य साझा नहीं करते हैं? ओ 'रेली की पुस्तक "पायथॉन फॉर यूनिक्स एंड लिनक्स सिस्टम एडमिनिस्ट्रेशन" के पेज 305 पर लिखा है: "... बिना कतार के थ्रेडिंग का इस्तेमाल करने से यह कई लोगों की तुलना में अधिक जटिल हो सकता है, यह वास्तविक रूप से संभालना बेहतर होता है। मॉड्यूल यदि आपको लगता है कि आपको थ्रेड्स का उपयोग करने की आवश्यकता है तो क्यों? क्यूंकि क्यू मॉड्यूल भी म्यूटेक्स के साथ डेटा को स्पष्ट रूप से संरक्षित करने की आवश्यकता को कम कर देता है क्योंकि क्यू पहले से ही म्यूटेक्स द्वारा आंतरिक रूप से संरक्षित है। " फिर, मैं इस पर आपकी बात का स्वागत करता हूं।
इगोरगानापल्स्की

इगोर: आप बिल्कुल सही हैं कि आपको एक लॉक का उपयोग करना चाहिए। मैंने इसे प्रतिबिंबित करने के लिए पोस्ट को संपादित किया है। उस ने कहा, अजगर के साथ व्यावहारिक अनुभव से पता चलता है कि आपको डेटा संरचनाओं को लॉक करने की आवश्यकता नहीं है, जिसे आप अपने थ्रेड्स से एटोमिक रूप से संशोधित करते हैं, जैसे कि list.append या हैश कुंजी के अलावा। कारण, मेरा मानना ​​है कि जीआईएल है, जो सूची प्रदान करता है जैसे कि परमाणु की डिग्री के साथ सूची। मैं वर्तमान में इसे सत्यापित करने के लिए एक परीक्षण चला रहा हूं (किसी सूची में संख्या 0-9999 को जोड़ने के लिए 10k थ्रेड्स का उपयोग करें, जांचें कि सभी अपग्रेड काम किए गए हैं)। लगभग 100 पुनरावृत्तियों के बाद परीक्षण विफल नहीं हुआ है।
एरिक गैरीसन

इगोर: मैं इस विषय पर एक और सवाल पूछ रहा हूँ: stackoverflow.com/questions/2740435/…
एरिक गैरीसन

7

यदि आप सर्वश्रेष्ठ प्रदर्शन प्राप्त करना चाहते हैं, तो आप थ्रेड्स के बजाय एसिंक्रोनस I / O का उपयोग करने पर विचार कर सकते हैं। हजारों ओएस थ्रेड्स के साथ जुड़ा ओवरहेड गैर-तुच्छ है और पायथन दुभाषिया के भीतर स्विच करने वाला संदर्भ इसके ऊपर और भी अधिक जोड़ता है। थ्रेडिंग निश्चित रूप से काम पूरा कर लेगी, लेकिन मुझे संदेह है कि एक अतुल्यकालिक मार्ग बेहतर समग्र प्रदर्शन प्रदान करेगा।

विशेष रूप से, मैं ट्विस्टेड लाइब्रेरी ( http://www.twistedmatrix.com ) में async वेब क्लाइंट का सुझाव दूंगा । इसमें एक सम्मिलित रूप से खड़ी सीखने की अवस्था है, लेकिन एक बार जब आप एसिंक्रोनस प्रोग्रामिंग की ट्विस्टेड शैली पर एक अच्छा हैंडल प्राप्त करते हैं, तो इसका उपयोग करना काफी आसान है।

ट्विस्टेड के अतुल्यकालिक वेब क्लाइंट एपीआई पर एक हॉव्टो यहां उपलब्ध है:

http://twistedmatrix.com/documents/current/web/howto/client.html


Rakis: मैं वर्तमान में अतुल्यकालिक और गैर-अवरुद्ध I / O में देख रहा हूं। मुझे इसे लागू करने से पहले बेहतर तरीके से सीखने की जरूरत है। एक टिप्पणी जो मैं आपके पोस्ट पर करना चाहता हूं, वह है "कम से कम (मेरे लिनक्स वितरण के तहत)" हजारों ओएस थ्रेड्स "स्पॉन करने के लिए। अधिकतम संख्या में धागे हैं जो पायथन आपको कार्यक्रम के टूटने से पहले स्पॉन करने की अनुमति देगा। और मेरे मामले में (CentOS 5 पर) अधिकतम थ्रेड की संख्या 303 है।
इगोरगानपोलस्की

यह जानकर अच्छा लगा। मैंने पायथन में एक से अधिक बार एक से अधिक बार स्पॉइंग करने की कोशिश नहीं की है, लेकिन मुझे उम्मीद है कि इससे पहले कि वह बमबारी कर सकता था, उससे अधिक बनाने में सक्षम होगा।
राकिस

6

एक तरकीब:

from twisted.internet import reactor, threads
from urlparse import urlparse
import httplib
import itertools


concurrent = 200
finished=itertools.count(1)
reactor.suggestThreadPoolSize(concurrent)

def getStatus(ourl):
    url = urlparse(ourl)
    conn = httplib.HTTPConnection(url.netloc)   
    conn.request("HEAD", url.path)
    res = conn.getresponse()
    return res.status

def processResponse(response,url):
    print response, url
    processedOne()

def processError(error,url):
    print "error", url#, error
    processedOne()

def processedOne():
    if finished.next()==added:
        reactor.stop()

def addTask(url):
    req = threads.deferToThread(getStatus, url)
    req.addCallback(processResponse, url)
    req.addErrback(processError, url)   

added=0
for url in open('urllist.txt'):
    added+=1
    addTask(url.strip())

try:
    reactor.run()
except KeyboardInterrupt:
    reactor.stop()

परीक्षण समय:

[kalmi@ubi1:~] wc -l urllist.txt
10000 urllist.txt
[kalmi@ubi1:~] time python f.py > /dev/null 

real    1m10.682s
user    0m16.020s
sys 0m10.330s
[kalmi@ubi1:~] head -n 6 urllist.txt
http://www.google.com
http://www.bix.hu
http://www.godaddy.com
http://www.google.com
http://www.bix.hu
http://www.godaddy.com
[kalmi@ubi1:~] python f.py | head -n 6
200 http://www.bix.hu
200 http://www.bix.hu
200 http://www.bix.hu
200 http://www.bix.hu
200 http://www.bix.hu
200 http://www.bix.hu

Pingtime:

bix.hu is ~10 ms away from me
godaddy.com: ~170 ms
google.com: ~30 ms

6
थ्रेडपूल के रूप में ट्विस्टेड का उपयोग करने से आपको इससे मिलने वाले अधिकांश लाभों की अनदेखी हो रही है। आपको इसके बजाय async HTTP क्लाइंट का उपयोग करना चाहिए।
जीन-पॉल कैल्डेरोन

1

थ्रेड पूल का उपयोग करना एक अच्छा विकल्प है, और यह काफी आसान बना देगा। दुर्भाग्य से, अजगर के पास एक मानक पुस्तकालय नहीं है जो थ्रेड पूल को अति आसान बनाता है। लेकिन यहां एक अच्छा पुस्तकालय है जिसे आपको शुरू करना चाहिए: http://www.chrisarndt.de/projects/threadpool/

उनकी साइट से कोड उदाहरण:

pool = ThreadPool(poolsize)
requests = makeRequests(some_callable, list_of_args, callback)
[pool.putRequest(req) for req in requests]
pool.wait()

उम्मीद है की यह मदद करेगा।


मेरा सुझाव है कि आप इस तरह से ThreadPool के लिए q_size निर्दिष्ट करें: ThreadPool (पूलसाइज़, q_size = 1000) ताकि आपके पास मेमोरी में 100000 WorkRequest ऑब्जेक्ट नहीं होंगे। "यदि" q_sizeकार्य अनुरोध कतार का आकार सीमित है और कतार पूर्ण होने पर थ्रेड पूल ब्लॉक हो जाता है और वह इसमें और कार्य अनुरोध डालने की कोशिश करता है ( putRequestविधि देखें ), जब तक कि आप इसके लिए सकारात्मक timeoutमान का उपयोग न करें putRequest। "
तरणाय कालमन

अब तक मैं थ्रेडपूल समाधान को लागू करने की कोशिश कर रहा हूं - जैसा कि सुझाव दिया गया है। हालाँकि, मैं मेक रेज़िस्टेंस फ़ंक्शन में पैरामीटर सूची को नहीं समझता। कुछ_अच्छे, list_of_args, कॉलबैक क्या है? शायद अगर मैंने एक वास्तविक कोड स्निपेट देखा, जो मदद करेगा। मुझे आश्चर्य है कि उस पुस्तकालय के लेखक ने कोई उदाहरण पोस्ट नहीं किया।
इगोरगानापोलस्की

some_callable आपका फ़ंक्शन है जो आपके सभी काम (http सर्वर से कनेक्ट) में किया जाता है। list_of_args तर्क है जो some_callabe में पारित किए जाएंगे। कॉलबैक एक फ़ंक्शन है जिसे श्रमिक धागा होने पर बुलाया जाएगा। यह दो तर्क लेता है, कार्यकर्ता वस्तु (वास्तव में इस के साथ अपने आप को चिंता करने की ज़रूरत नहीं है), और परिणाम जो कार्यकर्ता ने प्राप्त किया।
केविन विस्किया

1

epollऑब्जेक्ट बनाएं ,
कई क्लाइंट टीसीपी सॉकेट खोलें,
अपने बफ़र्स को रिक्वेस्ट हेडर की तुलना में थोड़ा अधिक होने के लिए एडजस्ट करें , रिक्वेस्ट हेडर
भेजें - यह तत्काल होना चाहिए, बस एक बफर में रजिस्टर करना epoll, ऑब्जेक्ट में सॉकेट रजिस्टर
करना , ओब्जेक्ट .pollपर epollकरना,
पहले 3 पढ़ें प्रत्येक सॉकेट से बाइट्स .poll, उसके बाद (फ्लश न करें)
लिखें , क्लाइंट सॉकेट बंद करें।sys.stdout\n

एक साथ खोले गए सॉकेट की संख्या सीमित करें - सॉकेट्स बनाए जाने पर त्रुटियों को संभालें। नया सॉकेट तभी बनाएं जब दूसरा बंद हो।
OS सीमाएँ समायोजित करें।
कुछ (कई नहीं) प्रक्रियाओं में forking की कोशिश करें: यह CPU को अधिक प्रभावी ढंग से उपयोग करने में मदद कर सकता है।


@IgorGanapolsky होना चाहिए। मुझे आश्चर्य होगा अन्यथा। लेकिन यह निश्चित रूप से प्रयोग की जरूरत है।
जॉर्ज सोवतोव

0

आपके मामले के लिए, थ्रेडिंग शायद चाल चलेगी जैसा कि आप शायद एक प्रतिक्रिया के लिए इंतजार कर रहे हैं। मानक पुस्तकालय में क्यू जैसे सहायक मॉड्यूल हैं जो मदद कर सकते हैं।

मैंने फ़ाइलों के समानांतर डाउनलोडिंग के साथ एक समान काम किया है और यह मेरे लिए काफी अच्छा था, लेकिन यह उस पैमाने पर नहीं था जिसके बारे में आप बात कर रहे हैं।

यदि आपका कार्य अधिक सीपीयू-बाउंड था, तो आप मल्टीप्रोसेसिंग मॉड्यूल को देखना चाहते हैं , जो आपको अधिक सीपीयू / कोर / थ्रेड्स का उपयोग करने की अनुमति देगा (अधिक प्रक्रियाएं जो एक दूसरे को ब्लॉक नहीं करेंगी क्योंकि लॉकिंग प्रति प्रक्रिया है)


केवल एक चीज जिसका मैं उल्लेख करना चाहूंगा, वह यह है कि कई प्रक्रियाओं को स्पॉन करना कई थ्रेड्स को स्पॉइंग करने की तुलना में अधिक महंगा हो सकता है। इसके अलावा, कई प्रक्रियाओं बनाम कई थ्रेड्स के साथ 100,000 HTTP अनुरोध भेजने में कोई स्पष्ट प्रदर्शन लाभ नहीं है।
इगोरगानपोलस्की

0

विंडमिल का उपयोग करने पर विचार करें , हालांकि विंडमिल शायद कई धागे नहीं कर सकता।

आप इसे 5 मशीनों पर एक हाथ से लुढ़के पायथन स्क्रिप्ट के साथ कर सकते हैं, हर एक पोर्ट 40000-60000 का उपयोग करके आउटबाउंड कनेक्ट कर रहा है, जो 100,000 कनेक्शन को खोलता है।

इसके अलावा, यह एक अच्छी तरह से पिरोया हुआ क्यूए ऐप जैसे ओपनस्टा के साथ एक नमूना परीक्षण करने में मदद कर सकता है ताकि प्रत्येक सर्वर को कितना संभाल सके, इसका अंदाजा लगाया जा सके।

इसके अलावा, LWP :: ConnCache क्लास के साथ सिंपल पर्ल का उपयोग करने की कोशिश करें। आप शायद उस तरह से अधिक प्रदर्शन (अधिक कनेक्शन) प्राप्त करेंगे।


0

यह मुड़ async वेब क्लाइंट बहुत तेजी से आगे बढ़ता है।

#!/usr/bin/python2.7

from twisted.internet import reactor
from twisted.internet.defer import Deferred, DeferredList, DeferredLock
from twisted.internet.defer import inlineCallbacks
from twisted.web.client import Agent, HTTPConnectionPool
from twisted.web.http_headers import Headers
from pprint import pprint
from collections import defaultdict
from urlparse import urlparse
from random import randrange
import fileinput

pool = HTTPConnectionPool(reactor)
pool.maxPersistentPerHost = 16
agent = Agent(reactor, pool)
locks = defaultdict(DeferredLock)
codes = {}

def getLock(url, simultaneous = 1):
    return locks[urlparse(url).netloc, randrange(simultaneous)]

@inlineCallbacks
def getMapping(url):
    # Limit ourselves to 4 simultaneous connections per host
    # Tweak this number, but it should be no larger than pool.maxPersistentPerHost 
    lock = getLock(url,4)
    yield lock.acquire()
    try:
        resp = yield agent.request('HEAD', url)
        codes[url] = resp.code
    except Exception as e:
        codes[url] = str(e)
    finally:
        lock.release()


dl = DeferredList(getMapping(url.strip()) for url in fileinput.input())
dl.addCallback(lambda _: reactor.stop())

reactor.run()
pprint(codes)

0

मैंने पाया कि tornadoपैकेज का उपयोग करना इसे प्राप्त करने का सबसे तेज़ और सरल तरीका है:

from tornado import ioloop, httpclient, gen


def main(urls):
    """
    Asynchronously download the HTML contents of a list of URLs.
    :param urls: A list of URLs to download.
    :return: List of response objects, one for each URL.
    """

    @gen.coroutine
    def fetch_and_handle():
        httpclient.AsyncHTTPClient.configure(None, defaults=dict(user_agent='MyUserAgent'))
        http_client = httpclient.AsyncHTTPClient()
        waiter = gen.WaitIterator(*[http_client.fetch(url, raise_error=False, method='HEAD')
                                    for url in urls])
        results = []
        # Wait for the jobs to complete
        while not waiter.done():
            try:
                response = yield waiter.next()
            except httpclient.HTTPError as e:
                print(f'Non-200 HTTP response returned: {e}')
                continue
            except Exception as e:
                print(f'An unexpected error occurred querying: {e}')
                continue
            else:
                print(f'URL \'{response.request.url}\' has status code <{response.code}>')
                results.append(response)
        return results

    loop = ioloop.IOLoop.current()
    web_pages = loop.run_sync(fetch_and_handle)

    return web_pages

my_urls = ['url1.com', 'url2.com', 'url100000.com']
responses = main(my_urls)
print(responses[0])

-2

सबसे आसान तरीका पायथन के अंतर्निहित थ्रेडिंग लाइब्रेरी का उपयोग करना होगा। वे "वास्तविक" / कर्नेल थ्रेड नहीं हैं उनके पास समस्याएँ हैं (जैसे क्रमबद्धता), लेकिन पर्याप्त अच्छे हैं। आप एक कतार और थ्रेड पूल चाहते हैं। एक विकल्प यहां है , लेकिन अपना खुद का लिखना तुच्छ है। आप सभी 100,000 कॉल को समानांतर नहीं कर सकते हैं, लेकिन आप एक ही समय में उनमें से 100 (या तो) फायर कर सकते हैं।


7
रूबी के उदाहरण के विपरीत, पायथन के धागे काफी वास्तविक हैं। हुड के तहत वे मूल ओएस थ्रेड्स के रूप में कार्यान्वित किए जाते हैं, कम से कम यूनिक्स / लिनक्स और विंडोज पर। हो सकता है कि आप GIL का जिक्र कर रहे हों, लेकिन इससे धागे कम असली नहीं
बनते

2
एली पाइथन के थ्रेड्स के बारे में सही है, लेकिन पेस्टिलेंस की बात जो आप थ्रेड पूल का उपयोग करना चाहते हैं, वह भी सही है। आखिरी चीज जो आप इस मामले में करना चाहते हैं, वह 100K अनुरोधों में से प्रत्येक के लिए एक साथ एक अलग थ्रेड शुरू करने का प्रयास है।
एडम क्रॉसलैंड

1
इगोर, आप समझदारी से टिप्पणियों में कोड स्निपेट्स पोस्ट नहीं कर सकते हैं, लेकिन आप अपने प्रश्न को संपादित कर सकते हैं और उन्हें वहां जोड़ सकते हैं।
एडम क्रॉसलैंड

महामारी: मेरे समाधान के लिए आप कितनी कतारें और धागे-प्रति-कतार सुझाएंगे?
इगोरगैनपोलस्की

इसके अलावा यह एक I / O बाउंड कार्य है जो सीपीयू बाउंड नहीं है, GIL काफी हद तक सीपीयू बाउंड कार्यों को प्रभावित करता है
PirateApp
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.