Requests: délai d'attente global

CrĂ©Ă© le 16 avr. 2016  Â·  38Commentaires  Â·  Source: psf/requests

Nous utilisons déjà largement le paramÚtre timeout qui permet de définir des délais d'attente par transaction TCP. C'est trÚs utile! Cependant, nous devons également prendre en charge un délai d'attente global sur la connexion. En lisant les documents sur les délais d'expiration, je vois que cela n'est pas actuellement pris en charge, et en recherchant les problÚmes au moins un peu en arriÚre, je n'ai pas vu d'autre demande pour cette fonctionnalité - excusez-moi s'il y en a.

Je me rends compte que nous pouvons définir des minuteries dans notre bibliothÚque pour accomplir cela, mais je suis préoccupé par la surcharge supplémentaire (une par thread, et nous pouvons en avoir plusieurs) ainsi que par les effets néfastes sur la mise en commun des connexions si nous finissons par devoir abandonner un demande. Existe-t-il un bon moyen d'abandonner une demande en premier lieu ? Je n'ai rien vu d'évident dans la doc.

Donc : Ă  long terme, ce serait formidable si nous pouvions ajouter un dĂ©lai d'expiration global Ă  la bibliothĂšque de requĂȘtes. À court terme, existe-t-il une mĂ©thode recommandĂ©e pour mettre cela en Ɠuvre de mon cĂŽté ?

Propose Close

Commentaire le plus utile

@jribbens Il y a quelques problĂšmes avec cela.

La partie 1 est que la complexitĂ© d'un tel patch est trĂšs Ă©levĂ©e. Pour qu'il se comporte correctement, vous devez modifier Ă  plusieurs reprises les dĂ©lais d'attente au niveau du socket. Cela signifie que le correctif doit ĂȘtre transmis de maniĂšre omniprĂ©sente via httplib, que nous avons dĂ©jĂ  corrigĂ© plus que nous ne le souhaiterions. Essentiellement, nous aurions besoin d'atteindre httplib et de rĂ©implĂ©menter environ 50% de ses mĂ©thodes plus complexes afin de rĂ©aliser ce changement fonctionnel.

La partie 2 est que la maintenance d'un tel patch est relativement lourde. Nous aurions probablement besoin de commencer Ă  maintenir ce qui Ă©quivaut Ă  un fork parallĂšle de httplib (plus correctement http.client pour le moment) afin de le faire avec succĂšs. Alternativement, nous aurions besoin de prendre en charge la charge de maintenance d'une pile HTTP diffĂ©rente qui se prĂȘte mieux Ă  ce type de changement. Cette partie est, je suppose, souvent manquĂ©e par ceux qui souhaitent disposer d'une telle fonctionnalité : le coĂ»t de sa mise en Ɠuvre est Ă©levĂ©, mais ce n'est rien comparĂ© aux coĂ»ts de maintenance permanents liĂ©s Ă  la prise en charge d'une telle fonctionnalitĂ© sur toutes les plates-formes.

La partie 3 est que l'avantage d'un tel patch n'est pas clair. D'aprĂšs mon expĂ©rience, la plupart des gens qui veulent un patch de dĂ©lai d'attente total ne pensent pas trĂšs clairement Ă  ce qu'ils veulent. Dans la plupart des cas, les paramĂštres de dĂ©lai d'attente total finissent par avoir pour effet de tuer des requĂȘtes parfaitement bonnes sans raison.

Par exemple, supposons que vous ayez conçu un morceau de code qui tĂ©lĂ©charge des fichiers et que vous souhaitiez gĂ©rer les blocages. Bien qu'il soit initialement tentant de vouloir dĂ©finir un dĂ©lai d'attente total fixe ("aucune requĂȘte ne peut prendre plus de 30 secondes !"), un tel dĂ©lai passe Ă  cĂŽtĂ© de l'essentiel. Par exemple, si un fichier passe de 30 Mo Ă  30 Go, un tel fichier ne peut _jamais_ ĂȘtre tĂ©lĂ©chargĂ© dans ce type d'intervalle de temps, mĂȘme si le tĂ©lĂ©chargement peut ĂȘtre entiĂšrement sain.

En d'autres termes, les dĂ©lais d'attente totaux sont une nuisance attrayante : ils semblent rĂ©soudre un problĂšme, mais ils ne le font pas efficacement. Une approche plus utile, Ă  mon avis, consiste Ă  tirer parti du dĂ©lai d'expiration de l'action par socket, combinĂ© Ă  stream=True et iter_content , et Ă  vous attribuer des dĂ©lais d'expiration pour les blocs de donnĂ©es. De la façon dont iter_content fonctionne, le flux de contrĂŽle sera renvoyĂ© Ă  votre code Ă  un intervalle assez rĂ©gulier. Cela signifie que vous pouvez dĂ©finir vous-mĂȘme des dĂ©lais d'attente au niveau du socket (par exemple, 5 s), puis iter_content sur des morceaux assez petits (par exemple, 1 Ko de donnĂ©es) et ĂȘtre relativement sĂ»r que, Ă  moins que vous ne soyez activement attaquĂ©, aucun dĂ©ni de service est possible ici. Si vous ĂȘtes vraiment prĂ©occupĂ© par le dĂ©ni de service, rĂ©glez votre dĂ©lai d'attente au niveau du socket beaucoup plus bas et votre taille de bloc plus petite (0,5 s et 512 octets) pour vous assurer que vous recevez rĂ©guliĂšrement le flux de contrĂŽle.

Le résultat de tout cela est que je crois que les délais d'attente totaux sont un défaut dans une bibliothÚque comme celle-ci. Le meilleur type de délai d'attente est celui qui est réglé pour laisser suffisamment de temps aux réponses volumineuses pour télécharger en paix, et un tel délai d'attente est mieux servi par les délais d'attente au niveau du socket et iter_content .

Tous les 38 commentaires

Salut @emgerner-msft,

Pour référence, voici toutes les variations sur ce thÚme si ce n'est cette demande de fonctionnalité exacte :

Nous en avons également discuté sur https://github.com/sigmavirus24/requests-toolbelt/issues/51

Vous remarquerez que le dernier lien traite de ce package qui devrait gĂ©rer cela pour vous sans l'ajouter aux demandes. La rĂ©alitĂ©, c'est qu'il n'y a pas besoin de requĂȘtes pour le faire alors qu'un autre paquet le fait dĂ©jĂ  trĂšs bien.

Le package que vous rĂ©fĂ©rencez le fait en crĂ©ant un processus distinct pour exĂ©cuter la requĂȘte Web. C'est un moyen trĂšs lourd d'atteindre l'objectif simple d'un dĂ©lai d'attente et, Ă  mon avis, ne remplace en aucun cas les demandes elles-mĂȘmes ayant une fonctionnalitĂ© de dĂ©lai d'attente native.

@jribbens Si vous pouviez trouver un moyen qui n'utilise ni threads ni processus, ce serait incroyable. Jusque-lĂ , si vous voulez une horloge murale, votre meilleur pari est ce paquet car c'est le moyen le plus fiable d'y parvenir pour le moment.

Je ne pense pas que @jribbens dise qu'il n'y a pas de threads ni de processus. Juste qu'une demande Web de processus _par_ est excessive. De nombreux langages permettent à plusieurs minuteurs de partager un seul thread ou processus supplémentaire. Je ne sais tout simplement pas comment faire cela au mieux en Python.

Il semble que # 1928 ait le plus de discussions sur les alternatives, mais la plupart comportent de nombreuses mises en garde (cela ne fonctionnera pas pour votre cas d'utilisation, etc.). Je suis d'accord pour avoir du code personnalisĂ© dans ma bibliothĂšque et Ă©crire ma propre solution personnalisĂ©e si cela n'appartient vraiment pas aux demandes, mais je pense que j'ai besoin d'un peu plus d'informations sur ce Ă  quoi cela ressemblerait. La raison pour laquelle nous utilisons des requĂȘtes est de s'Ă©loigner de la logique de regroupement de connexions TCP de bas niveau, mais il semble que lire ce fil que pour Ă©crire ce code personnalisĂ©, j'ai besoin de connaĂźtre cette logique, et c'est ce que j'ai des problĂšmes avec .

@emgerner-msft est correct. Je suis un peu confus par le commentaire de @ sigmavirus24 , avoir un "timeout total" sans utiliser de threads ou de processus semble assez banal et pas du tout "incroyable". Calculez simplement le délai au début de l'ensemble du processus (par exemple, deadline = time.time() + total_timeout ) puis, pour toute opération individuelle, définissez le délai d'attente sur deadline - time.time() .

avoir un "timeout total" sans utiliser de threads ou de processus semble assez banal et pas du tout "incroyable".

Et votre solution est plutĂŽt primitive. La raison pour laquelle _la plupart des gens_ veulent un dĂ©lai d'expiration total (ou mural) est d'empĂȘcher une lecture de "se bloquer", en d'autres termes un cas comme celui-ci :

r = requests.get(url, stream=True)
for chunk in r.iter_content(chunksize):
    process_data(chunk)

OĂč chaque lecture prend beaucoup de temps au milieu de iter_content mais c'est moins que le dĂ©lai de lecture (je suppose que nous appliquons cela lors de la diffusion, mais il se peut toujours que nous ne le fassions pas), ils ont spĂ©cifiĂ© . Certes, il semblerait que cela devrait ĂȘtre simplement gĂ©rĂ© par votre solution @jribbens jusqu'Ă  ce que vous vous rappeliez comment les horloges dĂ©rivent et que l'heure d'Ă©tĂ© fonctionne et que time.time() est terriblement insuffisant.

Enfin, il est important de garder Ă  l'esprit que l'API de Requests est gelĂ©e. Il n'y a pas d'API bonne ou cohĂ©rente pour spĂ©cifier un dĂ©lai d'expiration total. Et si nous implĂ©mentions un dĂ©lai d'attente comme vous le suggĂ©rez, nous aurions d'innombrables bogues pour lesquels ils spĂ©cifiaient un dĂ©lai d'attente total d'une minute, mais cela a pris plus de temps car la derniĂšre fois que nous avons vĂ©rifiĂ©, nous Ă©tions sous une minute, mais leur dĂ©lai de lecture configurĂ© Ă©tait suffisamment long pour que leur dĂ©lai d'attente erreur a Ă©tĂ© soulevĂ©e environ une minute et demie. C'est un dĂ©lai d'attente de mur _trĂšs_ approximatif qui serait lĂ©gĂšrement meilleur pour les personnes qui recherchent cela, mais pas diffĂ©rent de la personne qui l'implĂ©mente elle-mĂȘme.

Toutes mes excuses si je n'étais pas clair @ sigmavirus24 , vous semblez avoir critiqué mon illustration de principe pseudocode comme si vous pensiez qu'il s'agissait d'un patch littéral. Je dois cependant souligner que time.time() ne fonctionne pas comme vous le pensez apparemment - l'heure d'été n'est pas pertinente, et le décalage d'horloge non plus sur les échelles de temps dont nous parlons ici. De plus, vous avez mal compris la suggestion si vous pensez que le bogue que vous décrivez se produirait. Enfin, je ne suis pas sûr de ce que vous entendez par l'API Requests étant "gelée" car l'API a été modifiée aussi récemment que la version 2.9.0, donc clairement ce que vous voulez dire, ce n'est pas ce que je comprendrais normalement par le mot.

Juste pour sĂ©parer ma discussion : je ne dis pas que c'est facile. Si c'Ă©tait totalement simple, je l'Ă©crirais et arrĂȘterais de vous embĂȘter. :)

Mes problĂšmes sont :
1) Tout sur les discussions que vous avez énumérées était des correctifs de singe. C'est bien, mais je l'utilise dans une bibliothÚque de qualité de production et je ne peux pas supporter la mise en garde des changements internes qui cassent tout.
2) Le dĂ©corateur de dĂ©lai d'attente dans le lien que vous avez donnĂ© est gĂ©nial, mais je ne sais pas comment cela affecte la connexion. MĂȘme si nous acceptons que la seule bonne façon de faire des dĂ©lais d'attente est avec un tas de threads, comment cette bibliothĂšque s'assure-t-elle que le socket est arrĂȘtĂ©, la connexion abandonnĂ©e, etc. Nous faisons beaucoup de connexions et cela semble potentiellement assez sujet aux fuites. les requĂȘtes n'ont pas de mĂ©thode "abandon" que je peux trouver (corrigez-moi si je me trompe), alors comment se passe l'arrĂȘt de la connexion ?

Tout ce que je recherche, c'est une version claire "bĂ©nie" de la façon de rĂ©soudre ce problĂšme par moi-mĂȘme, ou s'il n'y a pas de solution parfaite, quelques solutions avec les mises en garde discutĂ©es. Cela a-t-il du sens?

@ emgerner-msft En supposant que vous utilisez CPython, l'arrĂȘt de la connexion se produira lorsque la demande ne se poursuivra plus. À ce stade, toutes les rĂ©fĂ©rences Ă  la connexion sous-jacente seront perdues et le socket sera fermĂ© et supprimĂ©.

@Lukasa D'accord, merci ! Comment la bibliothĂšque dĂ©termine-t-elle que la demande ne se poursuit plus ? Par exemple, si j'utilisais la route du dĂ©corateur de dĂ©lai d'attente et que je coupais au milieu du tĂ©lĂ©chargement, quand le tĂ©lĂ©chargement s'arrĂȘterait-il rĂ©ellement ? Dois-je faire quelque chose de spĂ©cial avec les options de streaming ?

Si vous utilisez le dĂ©corateur de dĂ©lai d'attente, le tĂ©lĂ©chargement s'arrĂȘtera lorsque le dĂ©lai d'attente se dĂ©clenchera. C'est parce que les signaux interrompent les appels systĂšme, ce qui signifie qu'il n'y aura plus d'appels dans la socket. Une fois que la requĂȘte n'est plus dans la portĂ©e (par exemple, la pile s'est dĂ©roulĂ©e Ă  l'extĂ©rieur de votre fonction requests.* ), c'est dans : CPython nettoiera l'objet de connexion et dĂ©truira la connexion. Aucune option de streaming spĂ©ciale n'est requise ici.

Parfait. Je suis bon pour fermer le fil alors, Ă  moins que d'autres n'aient plus Ă  dire.

En fait, dĂ©solĂ©, encore un souci. Je regardais de plus prĂšs le code du dĂ©corateur de dĂ©lai d'attente depuis que vous avez dit qu'il utilise des signaux Ă©tait pertinent, par opposition Ă  quelque chose comme Python Timers (vraisemblablement). Il semble qu'il appelle signal avec SIGALRM qui est documentĂ© dans Python Signal pour ne pas fonctionner sous Windows. J'ai besoin que cela fonctionne dans les environnements Unix et Windows, ainsi que dans Python 2.7 et 3.3+ (un peu comme les demandes elles-mĂȘmes). Je vais fouiller un peu plus et voir si cela fonctionnera rĂ©ellement Ă©tant donnĂ© cela.

@ emgerner-msft C'est frustrant. =(

@Lukasa Yup, a essayĂ© l' extrait d'utilisation de base et cela ne fonctionne pas sous Windows. J'ai lu un peu plus de code/d'exemples et j'ai tripotĂ© et il semble que si nous n'utilisons pas de signaux, le paquet pourrait fonctionner, mais tout doit ĂȘtre sĂ©lectionnable, ce qui n'est pas le cas pour mon application. Donc, pour autant que je sache, le dĂ©corateur de dĂ©lai d'attente ne rĂ©soudra pas mon problĂšme. D'autres idĂ©es ?

@emgerner-msft Êtes-vous sĂ»r qu'aucun des signaux spĂ©cifiques Ă  Windows ne convient ?

@Lukasa Pour ĂȘtre franc, je ne sais tout simplement pas. Je n'ai jamais utilisĂ© de signaux auparavant, et tout comme je ne m'en suis pas rendu compte jusqu'Ă  ce que vous me disiez qu'ils interrompraient la demande, je ne sais pas ce qui est appropriĂ©. Je n'essaie pas non plus de faire fonctionner cela uniquement sous Windows. J'ai besoin d'une prise en charge complĂšte de crossplat (Windows et Unix) et de la prise en charge de Python 2 et Python 3. Tant de signaux semblent spĂ©cifiques Ă  la plate-forme, ça me jette. La minuterie Ă©tait l'une des solutions que j'envisageais qui semblait moins de bas niveau et pouvait donc prendre en charge mes contraintes, mais je ne sais pas alors comment je pourrais fermer la connexion. Je peux faire plus de lecture, mais c'est pourquoi j'espĂ©rais obtenir des conseils supplĂ©mentaires de votre part. :)

C'est donc un endroit vraiment délicat.

La réalité est qu'il n'y a plus ou moins aucun moyen multiplateforme de tuer un thread, sauf en l'interrompant, ce qui est essentiellement ce qu'est un signal. Cela signifie, je pense, que les signaux sont le seul moyen dont vous disposez pour que cela fonctionne sur toutes les plateformes. Je suis enclin à essayer d'envoyer un ping à un expert de Windowsy Pythony : @brettcannon , avez-vous une bonne suggestion ici ?

Par intĂ©rĂȘt, y a-t-il une raison de ne pas implĂ©menter le "dĂ©lai d'expiration total" dans les demandes autre que cette mise en Ɠuvre et son test nĂ©cessitent du travail ? Je veux dire, si un correctif pour l'implĂ©menter apparaissait comme par magie aujourd'hui, serait-il en thĂ©orie rejetĂ© ou acceptĂ© ? J'apprĂ©cie et suis d'accord avec le point de vue "Ă©liminer la complexitĂ© inutile", mais "vous pouvez le faire en bifurquant un processus sĂ©parĂ©" ne rend pas cette fonctionnalitĂ© inutile Ă  mon avis.

@jribbens Il y a quelques problĂšmes avec cela.

La partie 1 est que la complexitĂ© d'un tel patch est trĂšs Ă©levĂ©e. Pour qu'il se comporte correctement, vous devez modifier Ă  plusieurs reprises les dĂ©lais d'attente au niveau du socket. Cela signifie que le correctif doit ĂȘtre transmis de maniĂšre omniprĂ©sente via httplib, que nous avons dĂ©jĂ  corrigĂ© plus que nous ne le souhaiterions. Essentiellement, nous aurions besoin d'atteindre httplib et de rĂ©implĂ©menter environ 50% de ses mĂ©thodes plus complexes afin de rĂ©aliser ce changement fonctionnel.

La partie 2 est que la maintenance d'un tel patch est relativement lourde. Nous aurions probablement besoin de commencer Ă  maintenir ce qui Ă©quivaut Ă  un fork parallĂšle de httplib (plus correctement http.client pour le moment) afin de le faire avec succĂšs. Alternativement, nous aurions besoin de prendre en charge la charge de maintenance d'une pile HTTP diffĂ©rente qui se prĂȘte mieux Ă  ce type de changement. Cette partie est, je suppose, souvent manquĂ©e par ceux qui souhaitent disposer d'une telle fonctionnalité : le coĂ»t de sa mise en Ɠuvre est Ă©levĂ©, mais ce n'est rien comparĂ© aux coĂ»ts de maintenance permanents liĂ©s Ă  la prise en charge d'une telle fonctionnalitĂ© sur toutes les plates-formes.

La partie 3 est que l'avantage d'un tel patch n'est pas clair. D'aprĂšs mon expĂ©rience, la plupart des gens qui veulent un patch de dĂ©lai d'attente total ne pensent pas trĂšs clairement Ă  ce qu'ils veulent. Dans la plupart des cas, les paramĂštres de dĂ©lai d'attente total finissent par avoir pour effet de tuer des requĂȘtes parfaitement bonnes sans raison.

Par exemple, supposons que vous ayez conçu un morceau de code qui tĂ©lĂ©charge des fichiers et que vous souhaitiez gĂ©rer les blocages. Bien qu'il soit initialement tentant de vouloir dĂ©finir un dĂ©lai d'attente total fixe ("aucune requĂȘte ne peut prendre plus de 30 secondes !"), un tel dĂ©lai passe Ă  cĂŽtĂ© de l'essentiel. Par exemple, si un fichier passe de 30 Mo Ă  30 Go, un tel fichier ne peut _jamais_ ĂȘtre tĂ©lĂ©chargĂ© dans ce type d'intervalle de temps, mĂȘme si le tĂ©lĂ©chargement peut ĂȘtre entiĂšrement sain.

En d'autres termes, les dĂ©lais d'attente totaux sont une nuisance attrayante : ils semblent rĂ©soudre un problĂšme, mais ils ne le font pas efficacement. Une approche plus utile, Ă  mon avis, consiste Ă  tirer parti du dĂ©lai d'expiration de l'action par socket, combinĂ© Ă  stream=True et iter_content , et Ă  vous attribuer des dĂ©lais d'expiration pour les blocs de donnĂ©es. De la façon dont iter_content fonctionne, le flux de contrĂŽle sera renvoyĂ© Ă  votre code Ă  un intervalle assez rĂ©gulier. Cela signifie que vous pouvez dĂ©finir vous-mĂȘme des dĂ©lais d'attente au niveau du socket (par exemple, 5 s), puis iter_content sur des morceaux assez petits (par exemple, 1 Ko de donnĂ©es) et ĂȘtre relativement sĂ»r que, Ă  moins que vous ne soyez activement attaquĂ©, aucun dĂ©ni de service est possible ici. Si vous ĂȘtes vraiment prĂ©occupĂ© par le dĂ©ni de service, rĂ©glez votre dĂ©lai d'attente au niveau du socket beaucoup plus bas et votre taille de bloc plus petite (0,5 s et 512 octets) pour vous assurer que vous recevez rĂ©guliĂšrement le flux de contrĂŽle.

Le résultat de tout cela est que je crois que les délais d'attente totaux sont un défaut dans une bibliothÚque comme celle-ci. Le meilleur type de délai d'attente est celui qui est réglé pour laisser suffisamment de temps aux réponses volumineuses pour télécharger en paix, et un tel délai d'attente est mieux servi par les délais d'attente au niveau du socket et iter_content .

Peut-ĂȘtre que @zooba a une idĂ©e car il sait rĂ©ellement comment fonctionne Windows. :)

(Indépendamment, l'une de mes choses préférées à faire est de mettre en place une chaßne d'experts en guirlande d'experts dans un problÚme GitHub.)

Haha, je connais déjà @zooba et @brettcannon. Je peux discuter avec eux ici ou en interne car une solution à cela les aiderait probablement aussi.

@emgerner-msft Je pensais que oui, mais je ne voulais pas présumer : MSFT est une grande organisation !

@Lukasa Je viens de lire à travers le mur de texte que vous venez d'écrire ci-dessus - intéressant ! Sur la discussion de stream=True et iter_content pour chronométrer les téléchargements, quelle est la maniÚre équivalente de gérer les téléchargements plus volumineux ?

_PS_ : Le paragraphe ci-dessus commençant par "Mettre une autre maniĂšre, .." est le genre de conseils que j'ai recherchĂ©s dans les docs. Étant donnĂ© le nombre de demandes que vous recevez pour un dĂ©lai d'expiration maximal (et vos raisons valables de ne pas le faire), peut-ĂȘtre que la meilleure chose Ă  faire est d'ajouter certaines de ces informations dans la documentation sur le dĂ©lai d'expiration ?

lol @lukasa Je comprends votre point sur la maintenance, qui Ă©tait dĂ©jĂ  dans mon esprit, mais sur "fonctionnalitĂ© contre dĂ©faut", j'ai bien peur d'ĂȘtre complĂštement opposĂ© Ă  vous. Je pense que quiconque ne veut pas un dĂ©lai d'attente total ne pense pas clairement Ă  ce qu'il veut, et j'ai du mal Ă  imaginer une situation oĂč ce que vous dĂ©crivez comme un bogue "le tĂ©lĂ©chargement de 30 Mo passe Ă  30 Go et Ă©choue donc" n'est pas en fait une caractĂ©ristique bĂ©nĂ©fique!

Vous pouvez comme vous le dites faire quelque chose d'un peu similaire (mais je suppose que sans la plupart des avantages d'un délai d'attente total) en utilisant stream=True mais je pensais que le but des demandes était qu'il gérait les choses pour vous ...

Je pensais que le but des demandes était qu'il gérait les choses pour vous

Il gÚre HTTP pour vous. Le fait que nous gérons déjà les délais de connexion et de lecture et que nous avons eu quelques exemptions à notre gel des fonctionnalités de plusieurs années est tangentiel à la discussion sur l'utilité, l'opportunité, la cohérence (sur plusieurs plates-formes) et la maintenabilité. Nous apprécions vos commentaires et votre opinion. Si vous avez de nouvelles informations à présenter, nous vous en serions reconnaissants.

Il peut Ă©galement ĂȘtre rĂ©vĂ©lateur que les demandes ne gĂšrent pas tout, par le nombre de demandes de fonctionnalitĂ©s rejetĂ©es sur ce projet et le fait qu'il existe un projet distinct mettant en Ɠuvre des modĂšles d'utilisation communs pour les utilisateurs (la ceinture Ă  outils des demandes). Si un dĂ©lai d'attente total appartient n'importe oĂč, il serait lĂ , mais encore une fois, il devrait fonctionner sur Windows, BSD, Linux et OSX avec une excellente couverture de test et sans que ce soit un cauchemar Ă  maintenir.

Sur la discussion de stream=True et iter_content pour chronométrer les téléchargements, quelle est la maniÚre équivalente de gérer les téléchargements plus volumineux ?

Définissez un générateur pour votre téléchargement et transmettez-le à data . Ou, si l'encodage fragmenté n'est pas un gagnant pour vous, définissez un objet de type fichier avec une méthode magique read et passez _that_ à data .

Permettez-moi d'Ă©laborer un peu. Si vous passez un gĂ©nĂ©rateur Ă  data , les requĂȘtes itĂ©reront dessus et enverront chaque morceau Ă  tour de rĂŽle. Cela signifie que pour envoyer des donnĂ©es, nous devrons nĂ©cessairement transmettre le flux de contrĂŽle Ă  votre code pour chaque morceau. Cela vous permet de faire ce que vous voulez pendant ce temps, y compris de lancer des exceptions pour abandonner complĂštement la demande.

Si, pour une raison quelconque, vous ne pouvez pas utiliser l'encodage de transfert fragmentĂ© pour vos tĂ©lĂ©chargements (peu probable, mais possible si le serveur en question est vraiment mauvais), vous pouvez faire de mĂȘme en crĂ©ant un objet de type fichier qui a une longueur, puis en faisant votre magie dans l'appel read , qui sera appelĂ© Ă  plusieurs reprises pour des morceaux de 8192 octets. Encore une fois, cela garantit que le flux de contrĂŽle passe par votre code par intermittence, ce qui vous permet d'utiliser votre propre logique.

PS: Le paragraphe ci-dessus commençant par "Mettre une autre maniĂšre, .." est le genre de conseils que j'ai recherchĂ©s dans les docs. Étant donnĂ© le nombre de demandes que vous recevez pour un dĂ©lai d'expiration maximal (et vos raisons valables de ne pas le faire), peut-ĂȘtre que la meilleure chose Ă  faire est d'ajouter certaines de ces informations dans les documents sur le dĂ©lai d'expiration ?

Je suppose_. De maniÚre générale, cependant, je suis toujours nerveux à l'idée de mettre un texte quelque peu défensif dans la documentation. Cela pourrait aller dans une FAQ, je suppose, mais un texte qui explique pourquoi nous _n'avons pas_ quelque chose est rarement utile dans la documentation. L'espace dans les documents serait mieux servi, je suppose, par une recette pour faire quelque chose.

Je pense que quiconque ne veut pas d'un dĂ©lai d'expiration total ne pense pas clairement Ă  ce qu'il veut, et j'ai du mal Ă  imaginer une situation oĂč ce que vous dĂ©crivez comme un bogue "le tĂ©lĂ©chargement de 30 Mo passe Ă  30 Go et Ă©choue donc" n'est pas en fait une caractĂ©ristique bĂ©nĂ©fique!

Hein, je ne suis pas :

  • gestionnaire de paquets (par exemple pip, qui utilise des requĂȘtes), oĂč les paquets peuvent varier Ă©normĂ©ment en taille de donnĂ©es
  • web scraper, qui peut s'exĂ©cuter sur plusieurs sites dont la taille varie Ă©normĂ©ment
  • un agrĂ©gateur de journaux qui tĂ©lĂ©charge les fichiers journaux Ă  partir d'hĂŽtes qui ont des niveaux trĂšs variables d'entre nous (et donc des tailles de fichiers journaux)
  • tĂ©lĂ©chargeur de vidĂ©os (les vidĂ©os peuvent varier Ă©normĂ©ment en taille)

En rĂ©alitĂ©, je pense que le cas oĂč le dĂ©veloppeur sait dans un ordre de grandeur Ă  quelle taille de fichier il aura affaire est le cas rare. Dans la plupart des cas, les dĂ©veloppeurs n'en ont aucune idĂ©e. Et gĂ©nĂ©ralement, je dirais qu'il est imprudent de faire des hypothĂšses sur ces tailles. Si vous avez des contraintes sur la taille du tĂ©lĂ©chargement, votre code doit dĂ©libĂ©rĂ©ment coder ces hypothĂšses (par exemple sous la forme de vĂ©rifications de la longueur du contenu), plutĂŽt que de les coder implicitement et de les mĂ©langer avec la bande passante du rĂ©seau de l'utilisateur afin que d'autres personnes lisant le code peut les voir clairement.

mais je pensais que le but des demandes Ă©tait qu'il s'occupait des choses pour vous...

Les requĂȘtes ne gĂšrent dĂ©libĂ©rĂ©ment pas tout pour les utilisateurs. Essayer de tout faire est une tĂąche impossible, et il est impossible de construire une bonne bibliothĂšque qui le fasse. Nous disons rĂ©guliĂšrement aux utilisateurs de descendre vers urllib3 afin de rĂ©aliser quelque chose.

Nous ne mettons du code dans les requĂȘtes que si nous pouvons le faire mieux ou plus proprement que la plupart des utilisateurs ne pourront le faire. Sinon, il n'y a aucune valeur. Je ne suis vraiment pas encore convaincu que le dĂ©lai d'attente total soit l'une de ces choses, en particulier compte tenu de ce que je perçois comme une utilitĂ© relativement marginale lorsqu'il est agrĂ©gĂ© dans notre base d'utilisateurs.

Cela dit, je suis prĂȘt Ă  ĂȘtre convaincu que j'ai tort : je n'ai tout simplement pas encore vu d'argument convaincant pour cela (et, pour vous Ă©viter la passe, "j'en ai besoin !" n'est pas un argument convaincant : faut donner des raisons!).

@sigmavirus24

Si un dĂ©lai d'attente total appartient n'importe oĂč, il serait lĂ , mais encore une fois, il devrait fonctionner sur Windows, BSD, Linux et OSX avec une excellente couverture de test et sans que ce soit un cauchemar Ă  maintenir.

D'accord!

@lukasa Je suppose que je pense que non seulement je le veux, mais en fait presque tous les utilisateurs le voudraient s'ils y pensaient (ou s'ils ne rĂ©alisent pas que ce n'est pas dĂ©jĂ  lĂ ). La moitiĂ© de vos scĂ©narios d'utilisation ci-dessus oĂč vous dites que cela devrait ĂȘtre Ă©vitĂ©, je dirais que c'est vital (scraper Web et agrĂ©gateur de journaux) - les deux autres c'est moins nĂ©cessaire car il y a probablement un utilisateur qui attend le rĂ©sultat qui peut annuler le tĂ©lĂ©chargement manuellement si Ils veulent. Tout ce qui s'exĂ©cute en arriĂšre-plan sans interface utilisateur et n'utilise pas de dĂ©lai d'expiration global est boguĂ© Ă  mon avis !

Je suppose que ma pensée est que non seulement je le veux, mais en fait presque tous les utilisateurs le voudraient s'ils y pensaient (ou s'ils ne réalisent pas que ce n'est pas déjà là).

@jribbens, nous avons plusieurs annĂ©es (plus d'une dĂ©cennie si vous combinez les expĂ©riences de nous trois) pour parler et comprendre les besoins de nos utilisateurs. Ce qui a Ă©tĂ© nĂ©cessaire pour presque tous les utilisateurs (au moins 98%) a Ă©tĂ© des dĂ©lais de connexion et de lecture. Nous comprenons qu'une minoritĂ© trĂšs active de nos utilisateurs souhaite un dĂ©lai d'expiration global. Étant donnĂ© ce que nous pouvons extrapoler comme Ă©tant la taille du groupe d'utilisateurs potentiels pour cette fonctionnalitĂ© par rapport Ă  la taille potentielle des utilisateurs n'ayant pas besoin de cette fonctionnalitĂ© et la complexitĂ© de la maintenance et du dĂ©veloppement de la fonctionnalitĂ©, ce n'est pas vraiment quelque chose que nous allons faire.

Si vous avez quelque chose de _nouveau_ Ă  partager, nous aimerions l'entendre, mais tout ce que vous avez dit jusqu'Ă  prĂ©sent, c'est qu'Ă  votre avis, tout ce qui utilise des requĂȘtes sans dĂ©lai d'expiration global est boguĂ© et je peux imaginer qu'il y a beaucoup d'utilisateurs qui serait offensĂ© par votre affirmation selon laquelle leurs dĂ©cisions de conception sont boguĂ©es. Alors, s'il vous plaĂźt, Ă©vitez d'insulter l'intelligence de nos utilisateurs.

@ sigmavirus24 Tout au long de ce fil, vous avez Ă©tĂ© inutilement condescendant, incendiaire et grossier, et je vous demande poliment d'arrĂȘter.

@Lukasa J'ai examiné en détail vos suggestions sur la façon de télécharger et de télécharger en streaming et de lire les documents sur ces sujets. Si vous pouviez valider mes hypothÚses/questions, ce serait formidable.

  1. Pour les tĂ©lĂ©chargements en streaming, si j'utilise quelque chose comme un dĂ©lai de lecture "(par exemple, 5 s) puis iter_content sur des morceaux assez petits (par exemple, 1 Ko de donnĂ©es)", cela signifie que la bibliothĂšque de requĂȘtes appliquera le dĂ©lai de 5 s pour chaque lecture de 1 Ko et le dĂ©lai d'attente s'il prend plus de 5s. Correct?
  2. Pour les tĂ©lĂ©chargements en continu, si j'utilise un gĂ©nĂ©rateur ou un objet semblable Ă  un fichier qui renvoie des blocs de donnĂ©es et que je rĂšgle le dĂ©lai de lecture sur 5 s, la bibliothĂšque de requĂȘtes appliquera le dĂ©lai de 5 s pour chaque bloc que je renvoie et le dĂ©lai d'expiration si cela prend plus de temps. Correct?
  3. Si je n'utilise pas de gĂ©nĂ©rateur pour le tĂ©lĂ©chargement et que je transmets simplement des octets directement, comment la bibliothĂšque de requĂȘtes dĂ©cide-t-elle d'appliquer le dĂ©lai de lecture que j'ai dĂ©fini ? Par exemple, si je passe un bloc de 4 Mo et un dĂ©lai de lecture de 5 s, quand exactement ce dĂ©lai de lecture est-il appliqué ?
  4. Si je n'utilise pas iter_content et que les requĂȘtes tĂ©lĂ©chargent simplement tout le contenu directement dans la requĂȘte avec un dĂ©lai de lecture de 5 s, quand exactement ce dĂ©lai de lecture est-il appliqué ?

J'ai une comprĂ©hension gĂ©nĂ©rale des sockets/TCP protocol/etc mais pas exactement comment urllib fonctionne avec ces concepts Ă  un niveau infĂ©rieur ou si les demandes font quelque chose de spĂ©cial en plus de transmettre les valeurs. Je veux comprendre exactement comment les dĂ©lais d'attente sont appliquĂ©s, car le simple fait de rĂ©cupĂ©rer le flux de contrĂŽle et d'appliquer mon propre schĂ©ma de dĂ©lai d'attente ne fonctionne pas Ă©tant donnĂ© les problĂšmes de crossplat avec la fin du thread. S'il y a du matĂ©riel de lecture supplĂ©mentaire pour rĂ©pondre Ă  mes questions, n'hĂ©sitez pas Ă  me rĂ©fĂ©rer! En tout cas, cela devrait ĂȘtre, espĂ©rons-le, ma derniĂšre sĂ©rie de questions. :)

Merci pour votre aide jusqu'Ă  maintenant.

@emgerner-msft D'accord :

  1. Non. C'est plus complexe que ça, malheureusement. Comme indiquĂ©, chaque dĂ©lai d'attente s'applique _par appel de socket_, mais nous ne pouvons pas garantir le nombre d'appels de socket dans un morceau donnĂ©. La raison assez complexe en est que la bibliothĂšque standard enveloppe le socket de sauvegarde dans un objet tampon (gĂ©nĂ©ralement quelque chose comme io.BufferedReader ). Cela fera autant d'appels recv_into que nĂ©cessaire jusqu'Ă  ce qu'il ait fourni suffisamment de donnĂ©es. Cela peut ĂȘtre aussi peu que zĂ©ro (s'il y a dĂ©jĂ  suffisamment de donnĂ©es dans la mĂ©moire tampon) ou autant que le nombre exact d'octets que vous avez reçus si le pair distant vous injecte un octet Ă  la fois. Nous ne pouvons vraiment rien faire Ă  ce sujet : en raison de la nature d'un appel read() contre un tel objet mis en mĂ©moire tampon, nous ne rĂ©cupĂ©rons mĂȘme pas le flux de contrĂŽle entre chaque appel recv_into .

Cela signifie que la _seule_ façon de garantir que vous n'obtiendrez pas plus d'une attente de n secondes est de faire iter_content avec une taille de morceau de 1 . C'est une façon absurdement inefficace de télécharger un fichier (passe beaucoup trop de temps dans le code Python), mais c'est la seule façon d'obtenir la garantie que vous souhaitez.

  1. Je crois aussi que la réponse à cette question est non. Nous n'avons actuellement aucune idée d'un délai d'attente _send_. La façon d'en obtenir un est d'utiliser socket.setdefaulttimeout .
  2. Les délais de lecture sont appliqués uniquement aux lectures, donc peu importe la façon dont vous passez le corps.
  3. Ce dĂ©lai de lecture souffre des mĂȘmes problĂšmes que le cas iter_content : si vous avez des demandes de tout tĂ©lĂ©charger, nous finirons par Ă©mettre autant d'appels recv_into que nĂ©cessaire pour tĂ©lĂ©charger le corps, et le dĂ©lai d'attente s'applique Ă  chacun tour Ă  tour.

Vous vous heurtez ici au problĂšme principal : les requĂȘtes ne se rapprochent tout simplement pas suffisamment du socket pour obtenir exactement ce que vous recherchez. Nous _pourrions_ ajouter un dĂ©lai d'envoi : il s'agit d'un travail de demande de fonctionnalitĂ©, et il ne souffre pas des mĂȘmes problĂšmes que le dĂ©lai de lecture, mais pour tout le reste, nous sommes bloquĂ©s car httplib insiste (Ă  juste titre) sur l'Ă©change Ă  une reprĂ©sentation de socket tamponnĂ©e, puis le reste de httplib utilise cette reprĂ©sentation tamponnĂ©e.

@Lukasa

Ah, quel gùchis, haha. Je pensais que c'était le cas, mais j'espérais vraiment que j'avais tort.

Tout d'abord, nous avons désespérément besoin d'un délai d'attente d'envoi. Je ne peux tout simplement pas dire à mes utilisateurs que leurs téléchargements peuvent se bloquer à l'infini et que nous n'avons pas de plan pour résoudre le problÚme. :/

Il semble que je sois dans une situation impossible à ce stade. Il n'y a pas de support de bibliothÚque pour le délai d'attente total (ce que je comprends). Il n'y a aucune garantie sur le fonctionnement exact du délai d'attente existant avec différentes tailles de blocs - s'il y en avait, je pourrais simplement résumer le temps : délai de connexion + délai de lecture * taille du bloc. Pouvoir interrompre le flux avec le mode flux et les générateurs est agréable, mais comme je n'ai pas de solution pour interrompre réellement les threads de maniÚre multiplateforme, cela n'aide pas non plus. Voyez-vous d'autres options pour aller de l'avant? Que font les autres utilisateurs pour résoudre ces problÚmes ?

Tout d'abord, nous avons désespérément besoin d'un délai d'attente d'envoi. Je ne peux tout simplement pas dire à mes utilisateurs que leurs téléchargements peuvent se bloquer à l'infini et que nous n'avons pas de plan pour résoudre le problÚme. :/

Ainsi, la logique de temporisation utilisĂ©e dans les requĂȘtes est fondamentalement celle d'urllib3, il devrait donc suffire d'y apporter le changement : n'hĂ©sitez pas Ă  ouvrir une demande de fonctionnalitĂ© et nous pourrons vous aider Ă  travers le changement. Et Ă  plus court terme, n'hĂ©sitez pas Ă  enquĂȘter en utilisant setdefaulttimeout .

Voyez-vous d'autres options pour aller de l'avant? Que font les autres utilisateurs pour résoudre ces problÚmes ?

Les options dont vous disposez ici dépendent de vos contraintes spécifiques.

Si vous _devez_ avoir un dĂ©lai d'attente dĂ©terministe (c'est-Ă -dire s'il doit vous ĂȘtre possible de garantir qu'une requĂȘte ne prendra pas plus de _n_ secondes), vous ne pouvez pas le faire facilement avec la bibliothĂšque standard Python telle qu'elle existe aujourd'hui. Dans Python 2.7, vous auriez besoin de patcher socket._fileobject pour vous permettre d'exĂ©cuter un dĂ©lai d'attente sĂ©quentiel pour chaque appel recv , mais dans Python 3, c'est encore plus difficile car vous devez patcher dans une classe dont l'implĂ©mentation est en C ( io.BufferedReader ), ce qui va ĂȘtre un cauchemar.

Sinon, la seule façon de l'obtenir est de désactiver la mise en mémoire tampon dans la bibliothÚque standard. Cela cassera httplib et tous nos correctifs en plus, qui supposent que nous pouvons faire un appel read(x) qui ne se comportera pas comme l'appel systÚme read sur un socket mais plutÎt comme le read syscall sur un fichier (c'est-à-dire, retourne une longueur déterministe).

En d'autres termes : si vous avez _besoin_ d'un délai d'attente déterministe, vous constaterez qu'un grand nombre de bibliothÚques sont tout simplement incapables de vous le fournir. Fondamentalement, s'ils utilisent httplib ou socket.makefile , vous n'aurez pas de chance : il n'y a tout simplement pas de moyen propre de garantir que le contrÎle vous revienne dans un délai défini, sauf pour l'émission répétée de longueur -1 lit. Vous _pouvez_ le faire, mais cela nuira à vos performances.

Vous avez donc un compromis ici : si vous voulez un dĂ©lai d'attente dĂ©terministe, la façon dont la mise en mĂ©moire tampon est implĂ©mentĂ©e dans la bibliothĂšque standard Python (et donc, dans les requĂȘtes) ne vous le rendra tout simplement pas disponible. Vous pouvez rĂ©cupĂ©rer cela en dĂ©sactivant la mise en mĂ©moire tampon et en rĂ©Ă©crivant le code, mais cela nuit potentiellement assez gravement Ă  vos performances, Ă  moins que vous ne rĂ©implĂ©mentiez la mise en mĂ©moire tampon d'une maniĂšre qui reconnaisse les dĂ©lais d'attente.

Vous pouvez viser à implémenter le code requis dans la bibliothÚque standard Python dans la classe BufferedReader : vous pouvez certainement demander aux gens de Python s'ils sont intéressés. Mais je ne retiendrais pas mon souffle.

Ainsi, la logique de temporisation utilisĂ©e dans les requĂȘtes est fondamentalement celle d'urllib3, il devrait donc suffire d'y apporter le changement : n'hĂ©sitez pas Ă  ouvrir une demande de fonctionnalitĂ© et nous pourrons vous aider Ă  travers le changement. Et Ă  plus court terme, n'hĂ©sitez pas Ă  enquĂȘter en utilisant setdefaulttimeout.

Demande de fonctionnalité dans urllib3 ou ici ? En ouvrira un (ou les deux) dÚs que possible.

Demande de fonctionnalité dans urllib3 : nous n'avons pas besoin d'exposer quoi que ce soit de nouveau dans les demandes.

Cette page vous a été utile?
0 / 5 - 0 notes