AIOHTTP antworten hängen sich weg

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Cryptus
User
Beiträge: 5
Registriert: Mittwoch 6. November 2019, 21:38

Hallo,

ich versuche mit meinem Skript aus der Webseite mehrere URL Statuse auszulesen um sie dann weiter zu verarbeiten,
die anfragen werden richtig gestellt leider aber hängen sich die antworten weg und das Skript braucht sehr lange, bis eine Antwort kommt. Man kann mit der "n_requests" Menge rumspielen und dadurch unterschiedlich viele antworten zurück zubekommen aber im Endeffekt hängt sich das Skript trotzdem auf.

Wo ist der Fehler?

Code: Alles auswählen

import asyncio
import aiohttp

n_requests = 100

async def make_request(session, req_n):
    url = f"https://tbit-web.de/exec/ximg.php?fid={req_n}"
    print(req_n)
    async with session.get(url) as resp:
        print(req_n, "finish")

async def main():
    async with aiohttp.ClientSession() as session:
        counternr = 0
        while counternr < 9999:
            await asyncio.gather(
                *[make_request(session, i) for i in range(counternr, counternr + n_requests)]
        )
            counternr= n_requests + counternr

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich sehe da nichts, das lange antworten frühzeitig abklemmt. Auf alles auf das du wartest muss reagiert werden. Also dauert es so lange wie es dauert. Was sonst sollte passieren?
Cryptus
User
Beiträge: 5
Registriert: Mittwoch 6. November 2019, 21:38

Komisch der Fehler scheint nur bei Ubuntu in PyCharm vorzukommen wenn ich es auf mein Windows Rechner ausführe funktioniert es einwandfrei, für mich ist es wichtig das es für Ubuntu auch funktioniert. Die beiden Python versionen sind auch gleich nämlich 3.6
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Vielleicht ”wehrt” sie auch irgendwann der Server wegen zu vieler automatisierter Zugriffe.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Cryptus hat geschrieben: Donnerstag 7. November 2019, 00:29 Komisch der Fehler scheint nur bei Ubuntu in PyCharm vorzukommen wenn ich es auf mein Windows Rechner ausführe funktioniert es einwandfrei, für mich ist es wichtig das es für Ubuntu auch funktioniert. Die beiden Python versionen sind auch gleich nämlich 3.6
Wie bereits erwähnt kann es sein, dass die Gegenstelle nach kurzer Zeit dicht macht, wenn deine Applikation zu viele Anfragen verschickt. Ein ähnliches Problem hatte ich auch mal, in meinem Falle half eine asyncio.Semaphore. Das sollte afaik auch helfen, wenn der Ubuntu-Rechner zu wenig RAM o.ä. hat.
When we say computer, we mean the electronic computer.
Cryptus
User
Beiträge: 5
Registriert: Mittwoch 6. November 2019, 21:38

Der Server nimmt die anfragen an und beatrwortet die ja auch auf Windows ohne Probleme mich wundert nur warum er das in Ubuntu nicht schafft ich ich werde wohl nochmal eine VM aufsetzen um das zu testen wenn jemand ein Linux system hat wäre schon wenn das jemand mittesten könnte.
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

Versuchst du es denn jeweils vom selben Rechner und änderst nur das Betriebssystem? Oder sind das unterschiedliche Rechner, unterschiedliche Python Versionen, unterschiedliche IPs?
Es gibt auch Seiten, die die Abrufe von IPs, die Rechenzentren zugeordnet sind, blocken.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe das Programm leicht modifiziert, und mit python3.7 unter Ubuntu 18.04 getestet. Laeuft problemlos (wenn man den SSL-Fehler ignoriert, so wie im Code und dazugehoerigen Bug beschrieben).

Code: Alles auswählen

import asyncio
import aiohttp
from itertools import count

REQUEST_COUNT = 10
TOTAL_COUNT = 100

import asyncio
import ssl
import sys

SSL_PROTOCOLS = (asyncio.sslproto.SSLProtocol,)

def ignore_aiohttp_ssl_eror(loop):
    """Ignore aiohttp #3535 / cpython #13548 issue with SSL data after close

    There is an issue in Python 3.7 up to 3.7.3 that over-reports a
    ssl.SSLError fatal error (ssl.SSLError: [SSL: KRB5_S_INIT] application data
    after close notify (_ssl.c:2609)) after we are already done with the
    connection. See GitHub issues aio-libs/aiohttp#3535 and
    python/cpython#13548.

    Given a loop, this sets up an exception handler that ignores this specific
    exception, but passes everything else on to the previous exception handler
    this one replaces.

    Checks for fixed Python versions, disabling itself when running on 3.7.4+
    or 3.8.

    """
    if sys.version_info >= (3, 7, 4):
        return

    orig_handler = loop.get_exception_handler()

    def ignore_ssl_error(loop, context):
        if context.get("message") in {
            "SSL error in data received",
            "Fatal error on transport",
        }:
            # validate we have the right exception, transport and protocol
            exception = context.get('exception')
            protocol = context.get('protocol')
            if (
                isinstance(exception, ssl.SSLError)
                and exception.reason == 'KRB5_S_INIT'
                and isinstance(protocol, SSL_PROTOCOLS)
            ):
                if loop.get_debug():
                    asyncio.log.logger.debug('Ignoring asyncio SSL KRB5_S_INIT error')
                return
        if orig_handler is not None:
            orig_handler(loop, context)
        else:
            loop.default_exception_handler(context)

    loop.set_exception_handler(ignore_ssl_error)



async def make_request(session, req_n):
    url = f"https://tbit-web.de/exec/ximg.php?fid={req_n}"
    print(req_n)
    async with session.get(url) as resp:
        print(req_n, "finish")

async def main():
    async with aiohttp.ClientSession() as session:
        for total in count(step=REQUEST_COUNT):
            await asyncio.gather(
                *[make_request(session, total + i) for i in range(REQUEST_COUNT)]
        )
            if total >= TOTAL_COUNT:
                break

loop = asyncio.get_event_loop()
ignore_aiohttp_ssl_eror(loop)
loop.run_until_complete(main())
Gestartet mit

Code: Alles auswählen

time pipenv run python asyncio-test.py
real	0m11,646s
user	0m0,576s
sys	0m0,094s
Cryptus
User
Beiträge: 5
Registriert: Mittwoch 6. November 2019, 21:38

sparrow hat geschrieben: Donnerstag 7. November 2019, 11:03 Versuchst du es denn jeweils vom selben Rechner und änderst nur das Betriebssystem? Oder sind das unterschiedliche Rechner, unterschiedliche Python Versionen, unterschiedliche IPs?
Es gibt auch Seiten, die die Abrufe von IPs, die Rechenzentren zugeordnet sind, blocken.
Es sind unterschiedliche Rechner Ubuntu ist ein Server was ich gemietet hab und der Windows Rechner ist bei mir Zuhause da werde ich wohl eine VM aufsetzen Heute abend und testen ob dort mit Ubuntu das Skript funktioniert
__deets__ hat geschrieben: Donnerstag 7. November 2019, 12:01 Ich habe das Programm leicht modifiziert, und mit python3.7 unter Ubuntu 18.04 getestet. Laeuft problemlos (wenn man den SSL-Fehler ignoriert, so wie im Code und dazugehoerigen Bug beschrieben).
Danke für deine Mühe ja das Skript funktioniert auf Windows wie schon vorher bemerkt problemlos nur bei den Ubuntu Server hängt es immer noch, wie vorher schon gesagt werde ich bei mir zuhause VM aufsetzen und da nochmal testen
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Generell ist das Design suboptimal finde ich, denn momentan dominiert der laengste Request eines Batches immer die Laufzeit von allen anderen desselben Batches. Das merkt man das der dann ne Weile wartet bis die naechsten 10 kommen in meinem Skript.

Ich hab's mal milde umgebaut, so das es nun 10 worker gibt, die aus einer Queue die gesamnten Auftraege abarbeiten. Der Gewinn ist nicht riesig, aber ~2 Sekunden auf 100 Requests.

Code: Alles auswählen

import asyncio
import aiohttp
from itertools import count

WORKERS = 10
TOTAL_COUNT = 100

import asyncio
import ssl
import sys

SSL_PROTOCOLS = (asyncio.sslproto.SSLProtocol,)

def ignore_aiohttp_ssl_eror(loop):
    """Ignore aiohttp #3535 / cpython #13548 issue with SSL data after close

    There is an issue in Python 3.7 up to 3.7.3 that over-reports a
    ssl.SSLError fatal error (ssl.SSLError: [SSL: KRB5_S_INIT] application data
    after close notify (_ssl.c:2609)) after we are already done with the
    connection. See GitHub issues aio-libs/aiohttp#3535 and
    python/cpython#13548.

    Given a loop, this sets up an exception handler that ignores this specific
    exception, but passes everything else on to the previous exception handler
    this one replaces.

    Checks for fixed Python versions, disabling itself when running on 3.7.4+
    or 3.8.

    """
    if sys.version_info >= (3, 7, 4):
        return

    orig_handler = loop.get_exception_handler()

    def ignore_ssl_error(loop, context):
        if context.get("message") in {
            "SSL error in data received",
            "Fatal error on transport",
        }:
            # validate we have the right exception, transport and protocol
            exception = context.get('exception')
            protocol = context.get('protocol')
            if (
                isinstance(exception, ssl.SSLError)
                and exception.reason == 'KRB5_S_INIT'
                and isinstance(protocol, SSL_PROTOCOLS)
            ):
                if loop.get_debug():
                    asyncio.log.logger.debug('Ignoring asyncio SSL KRB5_S_INIT error')
                return
        if orig_handler is not None:
            orig_handler(loop, context)
        else:
            loop.default_exception_handler(context)

    loop.set_exception_handler(ignore_ssl_error)



async def make_worker(session, queue):
    while True:
        try:
            req_n = queue.get_nowait()
            url = f"https://tbit-web.de/exec/ximg.php?fid={req_n}"
            async with session.get(url) as resp:
                print(req_n, "finish")
        except asyncio.QueueEmpty:
            break

async def main():
    async with aiohttp.ClientSession() as session:
        queue = asyncio.Queue()
        workers = [make_worker(session, queue) for _ in range(WORKERS)]

        for request_no in range(TOTAL_COUNT):
            queue.put_nowait(request_no)
        await asyncio.gather(*workers)

loop = asyncio.get_event_loop()
ignore_aiohttp_ssl_eror(loop)
loop.run_until_complete(main())
Cryptus
User
Beiträge: 5
Registriert: Mittwoch 6. November 2019, 21:38

Da bin ich wieder, habe es jetzt auf einer VM getestet und sehe da, es funktioniert also hat mein Ubuntu Server probleme ich hatte dort auch Probleme beim installieren von AIOHTTP bibliothek auf Python 3.8 deswegen hab ich es auf 3.6 liegen lassen aber auf der VM ging alles reibungsloss zu installieren deswegen würde ich erste mal sagen fixed ich werde mir wohl ein anderen Server mieten müssen.
Benutzeravatar
sparrow
User
Beiträge: 4535
Registriert: Freitag 17. April 2009, 10:28

Nochmal der Hinweis, falls du ihn oben überlesen hast: Es gibt auch Webserver, die Zugriffe von IPs aus Server-Ranges verhungern lassen, weil die das nicht wollen.
Benutzeravatar
__blackjack__
User
Beiträge: 14019
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Es kann übrigens auch umgekehrt sein: Wenn man irgendwo einen Webserver mietet kann der Anbieter es eventuell auch nicht mögen wenn man über den andere Webseiten abgrast.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Antworten