#python #python-3.x #sockets #tenacity
Вопрос:
У меня есть запрос, который может выполняться только один раз. Иногда запрос занимает гораздо больше времени, чем следует.
Если бы я установил значение тайм-аута сокета по умолчанию (с помощью socket.setdefaulttimeout(5)
), и это заняло более 5 секунд, будет ли исходный запрос отменен, чтобы можно было безопасно повторить попытку (см. Пример кода ниже)?
Если нет, то каков наилучший способ отменить исходный запрос и повторить его снова, гарантируя, что он никогда не будет выполняться более одного раза?
import socket
from googleapiclient.discovery import build
from tenacity import retry, stop_after_attempt, wait_fixed, retry_if_exception_type
@retry(
retry=retry_if_exception_type(socket.timeout),
wait=wait_fixed(4),
stop=stop_after_attempt(3)
)
def create_file_once_only(creds, body):
service = build('drive', 'v3', credentials=creds)
file = service.files().create(body=body, fields='id').execute()
socket.setdefaulttimeout(5)
create_file_once_only(creds, body)
Комментарии:
1. В документации, похоже, ничего не сказано.
Ответ №1:
Маловероятно, что это может сработать так, как вы надеетесь. HTTP-СООБЩЕНИЕ (как и любой другой HTTP-запрос) реализуется путем отправки команды на веб-сервер, а затем получения ответа. Библиотека python requests
инкапсулирует для вас множество утомительных частей, но по сути она будет выполнять сокет send
, за которым последует сокет recv
(конечно, для этого может потребоваться несколько send
или recv
в зависимости от размера данных).
Теперь, если вы изначально смогли подключиться к веб-серверу (опять же, об этом заботится requests
библиотека, но обычно это занимает всего несколько миллисекунд), то весьма вероятно, что данные в вашем почтовом запросе уже давно отправлены. (Если отправляемые вами данные имеют длину в мегабайтах, возможно, что они были отправлены только частично, но если они достаточно короткие, они почти наверняка были отправлены полностью.)
Это, в свою очередь, означает, что, по всей вероятности, сервер получил весь ваш запрос и работает над ним или поставил ваш запрос в очередь, чтобы в конечном итоге над ним поработать. В любом случае, даже если вы разорвете соединение с сервером , выполнив тайм-аут recv
, маловероятно, что сервер действительно заметит это, пока не дойдет до того момента, когда он отправит свой ответ на ваш запрос. К этому моменту он, вероятно, закончил делать то, что собирался.
Другими словами, ваш тайм-аут сокета не будет применяться к «HTTP-запросу» — вместо этого он применяется к базовым операциям сокета-и почти наверняка к recv
части в конце. И просто разрыв соединения с сокетом не отменяет HTTP — запрос.
Не существует надежного способа сделать то, что вы хотите, без разработки протокола транзакций при тесном сотрудничестве с HTTP-сервером.
Вы могли бы сделать что-то (все еще в сотрудничестве с HTTP-сервером), что могло бы сделать что-то приближенное к этому:
- Создайте уникальный идентификатор (UUID или что-то подобное)
- Отправьте запрос на сервер, содержащий этот UUID вместе с другой информацией об учетной записи (имя, пароль, что угодно еще).
- Затем сервер создает учетную запись только в том случае, если он еще не создал учетную запись с тем же уникальным идентификатором.
Таким образом, вы можете запросить операцию несколько раз, но знайте, что на самом деле она будет выполнена только один раз. Если попросить выполнить ту же операцию во второй раз, сервер просто ответит «да, уже сделал это».