Requests: общий тайм-аут

Созданный на 16 апр. 2016  ·  38Комментарии  ·  Источник: psf/requests

Мы уже широко используем параметр тайм-аута, который позволяет устанавливать тайм-ауты транзакций TCP. Это очень полезно! Однако нам также необходимо поддерживать общий тайм-аут соединения. Читая документы по тайм -аутам, я вижу, что в настоящее время это не поддерживается, и, просматривая проблемы хотя бы немного назад, я не видел другого запроса на эту функцию - извините, если есть.

Я понимаю, что для этого можно установить таймеры в нашей библиотеке, но меня беспокоят дополнительные накладные расходы (по одному на поток, а у нас их может быть много), а также любые неблагоприятные последствия для пула соединений, если нам в конечном итоге потребуется прервать запрос. Есть ли хороший способ прервать запрос в первую очередь? Я не видел ничего очевидного в документах.

Итак: в долгосрочной перспективе было бы здорово, если бы мы могли добавить общее время ожидания в библиотеку запросов. В краткосрочной перспективе есть ли рекомендуемый способ реализации этого с моей стороны?

Propose Close

Самый полезный комментарий

@jribbens С этим есть несколько проблем.

Часть 1 заключается в том, что сложность такого патча очень высока. Чтобы заставить его вести себя правильно, вам нужно неоднократно менять тайм-ауты на уровне сокета. Это означает, что патч должен распространяться повсеместно через httplib, который мы уже исправили больше, чем нам хотелось бы. По сути, нам нужно было бы обратиться к httplib и повторно реализовать около 50% его более сложных методов, чтобы добиться этого функционального изменения.

Часть 2 заключается в том, что обслуживание такого патча относительно обременительно. Нам, вероятно, потребуется начать поддерживать параллельный форк httplib (точнее, http.client в настоящее время), чтобы успешно это сделать. В качестве альтернативы нам пришлось бы взять на себя бремя обслуживания другого стека HTTP, более подходящего для такого рода изменений. Я подозреваю, что эта часть обычно упускается из виду теми, кто хочет иметь такую ​​функцию: стоимость ее реализации высока, но это _ничто_ по сравнению с текущими затратами на поддержку такой функции на всех платформах.

Часть 3 заключается в том, что преимущество такого патча неясно. По моему опыту, большинство людей, которым нужен патч с полным тайм-аутом, не совсем ясно представляют, чего они хотят. В большинстве случаев параметры полного тайм-аута в конечном итоге приводят к тому, что совершенно хорошие запросы просто убиваются.

Например, предположим, что вы разработали небольшой код для загрузки файлов и хотите обрабатывать зависания. Хотя изначально заманчиво установить фиксированный общий тайм-аут («ни один запрос не может занимать более 30 секунд!»), такой тайм-аут упускает суть. Например, если размер файла изменится с 30 МБ на 30 ГБ, такой файл _никогда_ не сможет загрузиться за такой временной интервал, даже если загрузка может быть полностью исправной.

Иными словами, полные тайм-ауты — привлекательная неприятность: кажется, что они решают проблему, но делают это неэффективно. На мой взгляд, более полезным подходом является использование тайм-аута для каждого сокета в сочетании с stream=True и iter_content и назначение тайм-аутов для фрагментов данных. Как работает iter_content , поток управления будет возвращаться вашему коду через несколько регулярных интервалов. Это означает, что вы можете установить себе тайм-ауты на уровне сокета (например, 5 с), а затем iter_content для довольно небольших фрагментов (например, 1 КБ данных) и быть относительно уверенным, что, если вы не подвергаетесь активной атаке, отказ в обслуживании не произойдет. здесь можно. Если вы действительно обеспокоены отказом в обслуживании, установите время ожидания на уровне сокета намного меньше, а размер блока меньше (0,5 с и 512 байт), чтобы обеспечить регулярную передачу потока управления обратно вам.

Результатом всего этого является то, что я считаю, что полные тайм-ауты являются ошибкой в ​​библиотеке, подобной этой. Лучший тип тайм-аута — это тот, который настроен так, чтобы у больших ответов было достаточно времени для спокойной загрузки, и такой тайм-аут лучше всего обслуживается тайм-аутами на уровне сокета и iter_content .

Все 38 Комментарий

Привет @emgerner-msft,

Для справки, ниже приведены все варианты этой темы, если не конкретный запрос функции:

Мы также обсуждали это на https://github.com/sigmavirus24/requests-toolbelt/issues/51.

Вы заметите, что последняя ссылка обсуждает этот пакет, который должен обрабатывать это для вас, не добавляя его в запросы. Реальность такова, что нет необходимости в запросах на это, когда другой пакет уже делает это очень хорошо.

Пакет, на который вы ссылаетесь, делает это, разветвляя отдельный процесс для запуска веб-запроса. Это очень тяжелый способ достижения простой цели тайм-аута, и, на мой взгляд, он никоим образом не заменяет сами запросы, имеющие встроенную функцию тайм-аута.

@jribbens Если бы вы могли придумать способ, в котором не используются ни потоки, ни процессы, это было бы потрясающе. До тех пор, если вы хотите тайм-аут настенных часов, лучше всего выбрать этот пакет, так как это самый надежный способ добиться этого на данный момент.

Я не думаю, что @jribbens говорит об отсутствии потоков и процессов. Просто процесс _за_ веб-запрос избыточен. Многие языки позволяют использовать несколько таймеров в одном дополнительном потоке или процессе. Я просто не знаю, как лучше всего это сделать в Python.

Кажется, что # 1928 больше всего обсуждается альтернатив, но большинство из них содержит множество предостережений (это не сработает для вашего варианта использования и т. д.). Я согласен иметь некоторый пользовательский код в своей библиотеке и писать собственное собственное решение, если это действительно не относится к запросам, но я думаю, что мне нужно немного больше информации о том, как это будет выглядеть. Вся причина, по которой мы используем запросы, заключается в том, чтобы уйти от логики пула TCP-соединений низкого уровня, но кажется, что чтение этого потока, чтобы написать этот собственный код, мне нужно знать эту логику, и это то, с чем у меня возникли некоторые проблемы. .

@emgerner-msft верен. Меня немного смущает комментарий @ sigmavirus24 : «полный тайм-аут» без использования потоков или процессов кажется довольно заурядным и совсем не «удивительным». Просто рассчитайте крайний срок в начале всего процесса (например, deadline = time.time() + total_timeout ), а затем для каждой отдельной операции установите тайм-аут равным deadline - time.time() .

наличие «полного тайм-аута» без использования потоков или процессов кажется довольно обыденным и совсем не «удивительным».

И ваше решение довольно примитивно. Причина, по которой _большинству_ людей нужен общий тайм-аут (или настенные часы), заключается в том, чтобы предотвратить «зависание» чтения, другими словами, случай, подобный следующему:

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

Где каждое чтение занимает много времени в середине iter_content , но это меньше времени ожидания чтения (я предполагаю, что мы применяем это при потоковой передаче, но все же может быть так, что мы этого не делаем), они указали . Конечно, может показаться, что это должно быть просто обработано вашим решением @jribbens , пока вы не вспомните, как дрейфуют часы и работает летнее время, а их time.time() прискорбно недостаточно.

Наконец, важно помнить, что API Requests заморожен. Не существует хорошего или согласованного API для указания общего времени ожидания. И если бы мы внедрили тайм-аут, как вы предлагаете, у нас было бы бесчисленное количество ошибок, которые они указали на общий тайм-аут в одну минуту, но это заняло больше времени, потому что в последний раз, когда мы проверяли, у нас было меньше минуты, но их настроенный тайм-аут чтения был достаточно длинным, чтобы их тайм-аут ошибка возникла примерно через полторы минуты. Это _очень_ грубый тайм-аут стены, который был бы немного лучше для людей, которые ищут это, но ничем не отличается от человека, реализующего это самостоятельно.

Извините, если я был неясен @sigmavirus24 , вы, кажется, раскритиковали мою иллюстрацию принципа псевдокода, как если бы вы думали, что это буквальный патч. Я должен отметить, однако, что time.time() не работает так, как вы, по-видимому, думаете - летнее время не имеет значения, а также нет перекоса часов в шкалах времени, о которых мы здесь говорим. Кроме того, вы неправильно поняли предложение, если думаете, что ошибка, которую вы описываете, произойдет. Наконец, я не уверен, что вы подразумеваете под «замороженным» API запросов, поскольку API был изменен совсем недавно, в версии 2.9.0, поэтому ясно, что бы вы ни имели в виду, это не то, что я обычно понимаю под этим словом.

Просто чтобы отделить мою дискуссию: я на самом деле не утверждаю, что это легко. Если бы это было совсем просто, я бы просто написал и перестал вас доставать. :)

Мои проблемы:
1) Все, что вы перечислили, было патчами для обезьян. Это нормально, но я использую это в библиотеке производственного качества и не могу принять во внимание внутренние изменения, ломающие все.
2) Декоратор тайм-аута в ссылке, которую вы дали, великолепен, но я не понимаю, как это влияет на соединение. Даже если мы согласимся с тем, что единственный хороший способ сделать тайм-ауты — это с кучей потоков, как эта библиотека обеспечивает закрытие сокета, разрыв соединения и т. д. Мы делаем много соединений, и это кажется потенциально довольно склонен к утечкам. запросы не имеют метода «отмена», который я могу найти (поправьте меня, если я ошибаюсь), так как же происходит отключение соединения?

Все, что я ищу, это четкая «благословенная» версия того, как решить эту проблему самостоятельно, или, если нет идеального решения, пару решений с обсужденными предостережениями. Имеет ли это смысл?

@emgerner-msft Если вы используете CPython, отключение соединения произойдет, когда запрос перестанет выполняться. В этот момент все ссылки на базовое соединение будут потеряны, а сокет будет закрыт и уничтожен.

@Лукаса Хорошо, спасибо! Как библиотека определяет, что запрос больше не выполняется? Например, если я использовал маршрут декоратора тайм-аута и прервал загрузку в середине, когда загрузка фактически остановится? Нужно ли делать что-то особенное с параметрами потоковой передачи?

Если вы используете декоратор тайм-аута, загрузка остановится, когда сработает тайм-аут. Это связано с тем, что сигналы прерывают системные вызовы, а это означает, что дальнейших вызовов в сокет не будет. Как только запрос больше не находится в области действия (например, стек раскрутился за пределы вашей функции requests.* ), это внутри: CPython очистит объект соединения и разорвет соединение. Никаких специальных параметров потоковой передачи там не требуется.

Идеально. Тогда я могу закрыть ветку, если другие не хотят больше говорить.

Собственно, извините, еще одно беспокойство. Более внимательно изучил код декоратора тайм-аута, так как вы сказали, что он использует сигналы, а не что-то вроде таймеров Python (предположительно). Похоже, он вызывает сигнал с SIGALRM , который задокументирован в Python Signal , чтобы не работать в Windows. Мне нужно, чтобы это работало как в средах Unix, так и в Windows, а также в Python 2.7 и 3.3+ (как и сами запросы). Я еще немного поковыряюсь и посмотрю, действительно ли это сработает, учитывая это.

@emgerner-msft Это расстраивает. знак равно

@Lukasa Да, попробовал базовый фрагмент использования, и он не работает в Windows. Я прочитал еще немного кода/примеров и поиграл, и похоже, что если мы не используем сигналы, пакет может работать, но все должно быть доступно для выбора, что не относится к моему приложению. Насколько я могу судить, декоратор тайм-аута не решит мою проблему. Любые другие идеи?

@emgerner-msft Вы уверены, что ни один из сигналов, специфичных для Windows, не подходит?

@ Лукаса Честно говоря, я просто не знаю. Раньше я не использовал сигналы, и во многом так же, как я не понимал, пока вы не сказали мне, что они прервут запрос, я не уверен, что уместно. Я также не пытаюсь заставить это работать только в Windows. Мне нужна полная кроссплатформенная поддержка (Windows и Unix), а также поддержка Python 2 и Python 3. Так много сигналов выглядят специфичными для платформы, что меня сбивает с толку. Таймер был одним из решений, которые я рассматривал, которые выглядели менее низкоуровневыми и, таким образом, могли бы позаботиться о моих ограничениях, но тогда я не уверен, как я мог закрыть соединение. Я могу читать больше, но именно поэтому я надеялся получить дополнительные указания от вас, ребята. :)

Так что это действительно непростое место.

Реальность такова, что не существует более или менее кросс-платформенного способа убить поток, кроме как прервать его, что в основном и является сигналом. Это означает, я думаю, что сигналы — единственный путь, который у вас есть, чтобы заставить это работать на разных платформах. Я склонен попытаться пропинговать Windowsy Pythony эксперта: @brettcannon , у вас есть хорошее предложение?

Из интереса, есть ли причина не реализовывать «полный тайм-аут» в запросах, кроме той, что для его реализации и тестирования требуется работа? Я имею в виду, если бы патч для его реализации волшебным образом появился сегодня, теоретически он был бы отклонен или принят? Я ценю и согласен с точкой зрения «устранить ненужную сложность», но «вы можете сделать это, разветвив отдельный процесс», на мой взгляд, не делает эту функцию ненужной.

@jribbens С этим есть несколько проблем.

Часть 1 заключается в том, что сложность такого патча очень высока. Чтобы заставить его вести себя правильно, вам нужно неоднократно менять тайм-ауты на уровне сокета. Это означает, что патч должен распространяться повсеместно через httplib, который мы уже исправили больше, чем нам хотелось бы. По сути, нам нужно было бы обратиться к httplib и повторно реализовать около 50% его более сложных методов, чтобы добиться этого функционального изменения.

Часть 2 заключается в том, что обслуживание такого патча относительно обременительно. Нам, вероятно, потребуется начать поддерживать параллельный форк httplib (точнее, http.client в настоящее время), чтобы успешно это сделать. В качестве альтернативы нам пришлось бы взять на себя бремя обслуживания другого стека HTTP, более подходящего для такого рода изменений. Я подозреваю, что эта часть обычно упускается из виду теми, кто хочет иметь такую ​​функцию: стоимость ее реализации высока, но это _ничто_ по сравнению с текущими затратами на поддержку такой функции на всех платформах.

Часть 3 заключается в том, что преимущество такого патча неясно. По моему опыту, большинство людей, которым нужен патч с полным тайм-аутом, не совсем ясно представляют, чего они хотят. В большинстве случаев параметры полного тайм-аута в конечном итоге приводят к тому, что совершенно хорошие запросы просто убиваются.

Например, предположим, что вы разработали небольшой код для загрузки файлов и хотите обрабатывать зависания. Хотя изначально заманчиво установить фиксированный общий тайм-аут («ни один запрос не может занимать более 30 секунд!»), такой тайм-аут упускает суть. Например, если размер файла изменится с 30 МБ на 30 ГБ, такой файл _никогда_ не сможет загрузиться за такой временной интервал, даже если загрузка может быть полностью исправной.

Иными словами, полные тайм-ауты — привлекательная неприятность: кажется, что они решают проблему, но делают это неэффективно. На мой взгляд, более полезным подходом является использование тайм-аута для каждого сокета в сочетании с stream=True и iter_content и назначение тайм-аутов для фрагментов данных. Как работает iter_content , поток управления будет возвращаться вашему коду через несколько регулярных интервалов. Это означает, что вы можете установить себе тайм-ауты на уровне сокета (например, 5 с), а затем iter_content для довольно небольших фрагментов (например, 1 КБ данных) и быть относительно уверенным, что, если вы не подвергаетесь активной атаке, отказ в обслуживании не произойдет. здесь можно. Если вы действительно обеспокоены отказом в обслуживании, установите время ожидания на уровне сокета намного меньше, а размер блока меньше (0,5 с и 512 байт), чтобы обеспечить регулярную передачу потока управления обратно вам.

Результатом всего этого является то, что я считаю, что полные тайм-ауты являются ошибкой в ​​библиотеке, подобной этой. Лучший тип тайм-аута — это тот, который настроен так, чтобы у больших ответов было достаточно времени для спокойной загрузки, и такой тайм-аут лучше всего обслуживается тайм-аутами на уровне сокета и iter_content .

Возможно, у @zooba есть идея, поскольку он действительно знает, как работает Windows. :)

(Кроме того, одно из моих любимых занятий — организовать гирляндную цепочку экспертов по проблеме на GitHub.)

Ха-ха, я уже знаю @zooba и @brettcannon. Я могу обсудить с ними здесь или внутренне, так как решение этой проблемы, вероятно, поможет и им.

@emgerner-msft Я полагал, что вы можете, но не хотел предполагать: MSFT — большая организация!

@Lukasa Просто читаю сквозь стену текста, которую вы только что написали выше - интересно! При обсуждении потоков=True и iter_content для загрузки времени, каков эквивалентный способ обработки больших загрузок?

_PS_: абзац выше, начинающийся с «Иными словами,..», — это то руководство, которое я искал в документах. Учитывая количество запросов, которые вы получаете для максимального тайм-аута (и ваши веские причины, по которым вы этого не делаете), может быть, лучше всего добавить часть этой информации в документы тайм -аута?

lol @lukasa Я понимаю вашу точку зрения насчет обслуживания, о которой я уже думал, но в вопросе «функция против неправильной функции», боюсь, я полностью против вас. Я думаю, что любой, кто _не_ хочет полного тайм-аута, не думает четко о том, чего он хочет, и мне трудно представить ситуацию, когда то, что вы описываете как ошибку "загрузка 30 МБ изменяется на 30 ГБ и, следовательно, завершается ошибкой", не является таковой. на самом деле полезная функция!

Вы можете, как вы говорите, сделать что-то похожее (но я подозреваю, что без большинства преимуществ полного тайм-аута), используя stream=True , но я думал, что смысл запросов в том, что он обрабатывает вещи для вас...

Я думал, что смысл запросов в том, что он обрабатывает вещи для вас

Он обрабатывает HTTP для вас. Тот факт, что мы уже обрабатываем тайм-ауты соединения и чтения и что у нас было несколько исключений из нашего замораживания функций на несколько лет, не имеет отношения к обсуждению полезности, желательности, согласованности (на нескольких платформах) и ремонтопригодности. Мы ценим ваши отзывы и ваше мнение. Если у вас есть новая информация для представления, мы будем признательны за это.

Это также может говорить о том, что запросы не обрабатывают все, по количеству отклоненных запросов функций в этом проекте и тому факту, что существует отдельный проект, реализующий общие шаблоны использования для пользователей (инструментарий запросов). Если общий тайм-аут должен быть где-то, он должен быть там, но опять же, он должен работать в Windows, BSD, Linux и OSX с отличным тестовым покрытием и без кошмара в обслуживании.

При обсуждении потоков=True и iter_content для загрузки времени, каков эквивалентный способ обработки больших загрузок?

Определите генератор для загрузки и передайте его data . Или, если фрагментированное кодирование не является для вас победителем, определите файлоподобный объект с помощью волшебного метода read и передайте _that_ в data .

Позвольте мне немного уточнить. Если вы передадите генератор data , запросы будут перебирать его и отправлять каждый фрагмент по очереди. Это означает, что для отправки данных нам обязательно нужно передать поток управления вашему коду для каждого фрагмента. Это позволяет вам делать все, что вы хотите в это время, в том числе генерировать исключения, чтобы полностью отменить запрос.

Если по какой-то причине вы не можете использовать фрагментированное кодирование передачи для своих загрузок (маловероятно, но возможно, если рассматриваемый сервер действительно плохой), вы можете сделать то же самое, создав файлоподобный объект, который имеет длину, а затем выполните magic в вызове read , который будет неоднократно вызываться для фрагментов размером 8192 байта. Опять же, это гарантирует, что поток управления периодически проходит через ваш код, что позволяет вам использовать собственную логику.

PS: абзац выше, начинающийся с «Иными словами,..», — это то руководство, которое я искал в документах. Учитывая количество запросов, которые вы получаете для максимального тайм-аута (и ваши веские причины, по которым вы этого не делаете), может быть, лучше всего добавить часть этой информации в документы тайм-аута?

Я предполагаю_. Вообще говоря, я всегда нервничаю по поводу включения в документацию защитного текста. Думаю, это могло бы войти в FAQ, но текст, объясняющий, почему у нас _не_ есть что-то, редко бывает полезен в документации. Я подозреваю, что место в документах было бы лучше заполнить рецептом того, как что-то сделать.

Я думаю, что любой, кто не хочет полного тайм-аута, не думает четко о том, чего он хочет, и мне трудно представить ситуацию, когда то, что вы описываете как ошибку «загрузка 30 МБ изменяется на 30 ГБ и, следовательно, терпит неудачу», не является на самом деле полезная функция!

Хех, я не:

  • менеджер пакетов (например, pip, который использует запросы), где пакеты могут сильно различаться по размеру данных
  • веб-скребок, который может работать с несколькими сайтами, которые сильно различаются по размеру
  • агрегатор журналов, который загружает файлы журналов с хостов, которые имеют сильно различающиеся уровни нас (и, следовательно, размеры файлов журналов)
  • загрузчик видео (видео могут сильно различаться по размеру)

На самом деле, я думаю, что случай, когда разработчик знает с точностью до одного порядка, с какими размерами файлов он будет иметь дело, является необычным случаем. В большинстве случаев разработчики понятия не имеют. И вообще я бы сказал, что делать предположения об этих размерах неразумно. Если у вас есть ограничения на размер загрузки, тогда ваш код должен преднамеренно кодировать эти предположения (например, в форме проверки длины содержимого), а не неявно кодировать их и смешивать с пропускной способностью сети пользователя, чтобы другие люди, читающие код может видеть их ясно.

но я думал, что смысл запросов в том, что он обрабатывает вещи для вас...

Запросы намеренно не обрабатывают все для пользователей. Попытка сделать все — невыполнимая задача, и невозможно создать хорошую библиотеку, которая это делает. Мы регулярно советуем пользователям переходить на urllib3, чтобы чего-то добиться.

Мы помещаем код в запросы только в том случае, если мы можем сделать это лучше или чище, чем это сможет сделать большинство пользователей. Если нет, то нет никакой ценности. Я действительно еще не убежден, что полное время ожидания является одной из таких вещей, особенно с учетом того, что я считаю относительно незначительной полезностью при агрегировании всей нашей пользовательской базы.

Тем не менее, я открыт для того, чтобы убедиться, что я не прав: я просто еще не видел убедительного аргумента в пользу этого (и, чтобы отвлечь вас на перевале, «мне это нужно!» не является убедительным аргументом: нужно привести какие-то причины!).

@sigmavirus24

Если общий тайм-аут должен быть где-то, он должен быть там, но опять же, он должен работать в Windows, BSD, Linux и OSX с отличным тестовым покрытием и без кошмара в обслуживании.

Согласованный!

@lukasa Я полагаю, я думаю, что не только я хочу этого, на самом деле почти все пользователи хотели бы этого, если бы подумали об этом (или они не понимают, что этого еще нет). Половина ваших сценариев использования выше, где вы говорите, что этого следует избегать, я бы сказал, что это жизненно важно (веб-скрейпер и агрегатор журналов), а два других менее необходимы, поскольку, вероятно, будет пользователь, ожидающий результата, который может отменить загрузку вручную, если они хотят. Все, что работает в фоновом режиме без пользовательского интерфейса и не использует общий тайм-аут, на мой взгляд, содержит ошибки!

Я полагаю, я думаю, что не только я хочу этого, на самом деле почти все пользователи хотели бы этого, если бы подумали об этом (или они не понимают, что этого еще нет).

@jribbens у нас есть несколько лет (более десяти лет, если вы объедините опыт всех нас троих) общения и понимания потребностей наших пользователей. Что было необходимо почти всем (по крайней мере 98%) пользователям, так это тайм-ауты подключения и чтения. Мы понимаем, что очень красноречивое меньшинство наших пользователей хочет полного тайм-аута. Учитывая то, что мы можем экстраполировать на размер группы потенциальных пользователей этой функции по сравнению с потенциальным количеством пользователей, которым эта функция не нужна, и сложностью обслуживания и разработки функции, на самом деле это не то, чем мы собираемся заниматься. сделать.

Если у вас есть что-то _новое_, чем бы вы хотели поделиться, мы хотели бы это услышать, но все, что вы сказали до сих пор, это то, что, по вашему мнению, все, что использует запросы без общего времени ожидания, содержит ошибки, и я могу себе представить, что есть много пользователей, которые обиделся бы на ваше утверждение, что их конструкторские решения глючат. Поэтому, пожалуйста, воздержитесь от оскорбления интеллекта наших пользователей.

@sigmavirus24 На протяжении всей этой темы вы были излишне снисходительны, подстрекательны и грубы, и я прошу вас вежливо, пожалуйста, прекратите.

@Lukasa Я подробно изучил ваши предложения о том, как выполнять потоковую загрузку и загрузку, и прочитал документы по этим темам. Если бы вы могли подтвердить мои предположения/вопросы, это было бы здорово.

  1. Для потоковых загрузок, если я использую что-то вроде тайм-аута чтения «(например, 5 с), а затем iter_content для довольно небольших фрагментов (например, 1 КБ данных)», это означает, что библиотека запросов будет применять тайм-аут 5 с для каждого чтения 1 КБ и тайм-аут, если он занимает более 5 с. Правильный?
  2. Для потоковой загрузки, если я использую генератор или объект, подобный файлу, который возвращает фрагменты данных, и я устанавливаю тайм-аут чтения на 5 с, библиотека запросов будет применять тайм-аут 5 с для каждого возвращаемого фрагмента и тайм-аут, если это займет больше времени. Правильный?
  3. Если я не использую генератор для загрузки и просто передаю байты напрямую, как библиотека запросов решает применить установленный мной тайм-аут чтения? Например, если я передаю фрагмент размером 4 МБ и тайм-аут чтения 5 с, когда именно применяется этот тайм-аут чтения?
  4. Если я не использую iter_content и просто заставляю запросы загружать весь контент непосредственно в запрос с тайм-аутом чтения 5 с, когда именно применяется этот тайм-аут чтения?

У меня есть общее представление о сокетах/протоколе TCP/и т. д., но я не совсем понимаю, как urllib работает с этими концепциями на более низком уровне, или если запросы делают что-то особенное, кроме передачи значений вниз. Я хочу точно понять, как применяются тайм-ауты, поскольку простое возвращение потока управления и применение моей собственной схемы тайм-аута не работает, учитывая кроссплатформенные проблемы с завершением потока. Если есть дополнительные материалы для чтения, чтобы ответить на мои вопросы, не стесняйтесь направлять меня! В любом случае, надеюсь, это будет мой последний набор вопросов. :)

Спасибо за вашу помощь.

@emgerner-msft Хорошо:

  1. Нет. Это сложнее, к сожалению. Как уже говорилось, каждый тайм-аут применяется _на вызов сокета_, но мы не можем гарантировать, сколько вызовов сокета находится в данном фрагменте. Довольно сложная причина этого заключается в том, что стандартная библиотека оборачивает вспомогательный сокет в буферный объект (обычно что-то вроде io.BufferedReader ). Это сделает столько вызовов recv_into , сколько необходимо, пока не предоставит достаточно данных. Это может быть от нуля (если в буфере уже достаточно данных) или столько же, сколько байтов, которые вы получили, если удаленный одноранговый узел передает вам по одному байту за раз. На самом деле мы мало что можем с этим поделать: из-за природы вызова read() для такого буферизованного объекта мы даже не получаем обратно поток управления между каждым вызовом recv_into .

Это означает, что _единственный_ способ гарантировать, что вы получите не более n секунд ожидания, состоит в том, чтобы выполнить iter_content с размером фрагмента 1 . Это абсурдно неэффективный способ загрузки файла (тратит слишком много времени на код Python), но это единственный способ получить желаемую гарантию.

  1. Я также считаю, что ответ на этот вопрос — нет. В настоящее время у нас нет понятия тайм-аута _send_. Чтобы получить его, используйте socket.setdefaulttimeout .
  2. Тайм-ауты чтения применяются только к чтению, поэтому не имеет значения, как вы передаете тело.
  3. Этот тайм-аут чтения страдает теми же проблемами, что и случай iter_content : если у вас есть запросы на загрузку всего, то в конечном итоге мы будем испускать столько вызовов recv_into , сколько необходимо для загрузки тела, и применяется тайм-аут. каждому по очереди.

Здесь вы сталкиваетесь с основной проблемой: запросы просто не подходят достаточно близко к сокету, чтобы достичь именно того, что вы ищете. Мы _могли_ добавить тайм-аут отправки: это рассмотрение запроса функции, и он не страдает теми же проблемами, что и тайм-аут чтения, но для всего остального мы застряли, потому что httplib настаивает (справедливо) на обмене в буферизованное представление сокета, а затем остальные httplib используют это буферизованное представление.

@Лукаса

Ах, какой беспорядок, ха-ха. Я думал, что это может быть так, но я действительно надеялся, что я был неправ.

Во-первых, нам отчаянно нужен тайм-аут отправки. Я просто не могу сказать своим пользователям, что их загрузки могут бесконечно зависать, и у нас нет плана по устранению этой проблемы. :/

Кажется, я сейчас в какой-то безвыходной ситуации. Нет поддержки библиотеки для полного тайм-аута (что я понимаю). Нет никаких гарантий того, как именно существующий тайм-аут работает с различными размерами фрагментов — если бы они были, я мог бы просто суммировать время: тайм-аут соединения + тайм-аут чтения * размер фрагмента. Возможность прерывать поток с помощью потокового режима и генераторов — это хорошо, но, поскольку у меня нет решения для фактического прерывания потоков кросс-платформенным образом, это тоже не помогает. Видите ли вы другие варианты продвижения вперед? Что делают другие пользователи для решения этих проблем?

Во-первых, нам отчаянно нужен тайм-аут отправки. Я просто не могу сказать своим пользователям, что их загрузки могут бесконечно зависать, и у нас нет плана по устранению этой проблемы. :/

Таким образом, логика тайм-аута, используемая в запросах, в основном аналогична urllib3, поэтому ее должно быть достаточно, чтобы внести изменения: не стесняйтесь открывать запрос функции, и мы можем помочь вам с изменением. А в краткосрочной перспективе не стесняйтесь исследовать, используя setdefaulttimeout .

Видите ли вы другие варианты продвижения вперед? Что делают другие пользователи для решения этих проблем?

Варианты, которые у вас есть здесь, зависят от ваших конкретных ограничений.

Если вы _должны_ иметь детерминированный тайм-аут (то есть, если у вас должна быть возможность гарантировать, что запрос займет не более _n_ секунд), то вы не можете легко сделать это со стандартной библиотекой Python в том виде, в каком она существует сегодня. В Python 2.7 вам нужно будет исправить socket._fileobject , чтобы вы могли запускать последовательный тайм-аут для каждого вызова recv , но в Python 3 это еще сложнее, потому что вам нужно исправить класс, реализация которого находится в C ( io.BufferedReader ), что будет кошмаром.

В противном случае единственный способ получить это — отключить буферизацию в стандартной библиотеке. Это сломает httplib и все наши патчи поверх него, которые предполагают, что мы можем сделать вызов read(x) , который будет вести себя не как системный вызов read в сокете, а как read Системный вызов

Другими словами: если вам _нужен_ детерминированный тайм-аут, вы обнаружите, что огромное количество библиотек просто не в состоянии предоставить его вам. По сути, если они используют httplib или socket.makefile , вам не повезет: просто нет четкого способа гарантировать, что управление вернется к вам в определенное время, за исключением многократной выдачи длины -1 читает. Вы _можете_ сделать это, но это повредит вашему выступлению.

Таким образом, у вас есть компромисс: если вам нужен детерминированный тайм-аут, то способ, которым буферизация реализована в стандартной библиотеке Python (и, следовательно, в запросах), просто не сделает это доступным для вас. Вы можете вернуть это, отключив буферизацию и переписав код, но это потенциально сильно повредит вашей производительности, если вы не перереализуете буферизацию таким образом, чтобы он признавал тайм-ауты.

Вы можете попытаться реализовать требуемый код в стандартной библиотеке Python в классе BufferedReader : вы определенно можете спросить у разработчиков Python, если они заинтересованы. Но я бы не стал задерживать дыхание.

Таким образом, логика тайм-аута, используемая в запросах, в основном аналогична urllib3, поэтому ее должно быть достаточно, чтобы внести изменения: не стесняйтесь открывать запрос функции, и мы можем помочь вам с изменением. А в краткосрочной перспективе не стесняйтесь исследовать, используя setdefaulttimeout.

Запрос функции в urllib3 или здесь? Откроется один (или оба) как можно скорее.

Запрос функции в urllib3: нам не нужно раскрывать что-либо новое в запросах.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги