0

我有一个只能运行一次的请求。有时,请求花费的时间比应有的要长得多。

如果我要设置一个默认的套接字超时值(使用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)
4

1 回答 1

1

这不太可能像您希望的那样工作。HTTP POST(与任何其他 HTTP 请求一样)是通过向 Web 服务器发送命令,然后接收响应来实现的。pythonrequests库为您封装了很多繁琐的部分,但在核心,它会先做一个套接字,send然后再做一个套接字recv(它当然可能需要多个sendrecv取决于数据的大小)。

现在,如果您最初能够连接到 Web 服务器(同样,这由requests库为您处理,但通常只需要几毫秒),那么您的 POST 请求中的数据很可能早已被发送。(如果您发送的数据是兆字节长,它可能只发送了部分,但如果它相当短,几乎可以肯定它已全部发送。)

这反过来意味着服务器很可能已经收到您的整个请求并正在处理它,或者已经将您的请求排入队列以最终处理它。在任何一种情况下,即使您通过在 上超时来中断与服务器的连接,recv服务器实际上也不太可能注意到这一点,直到它执行到它将发送对您的请求的响应的点。到那时,它可能已经完成了它打算做的任何事情。

换句话说,您的套接字超时不会应用于“HTTP 请求”——而是应用于底层套接字操作——并且几乎可以肯定地应用于recv尾端的部分。并且仅仅断开套接字连接并不会取消 HTTP 请求。

如果不设计一个与 HTTP 服务器密切合作的事务协议,就没有可靠的方法来做你想做的事。

您可以做一些事情(仍然与 HTTP 服务器合作)可以做一些近似的事情:

  1. 创建唯一 ID(UUID 等)
  2. 向包含该 UUID 以及其他帐户信息(名称、密码等)的服务器发送请求
  3. 然后,服务器仅在尚未创建具有相同唯一 ID 的帐户时才创建该帐户。

这样,您可以多次请求该操作,但知道它实际上只会执行一次。如果要求第二次执行相同的操作,服务器将简单地响应“是的,已经这样做了”。

于 2021-08-16T22:44:09.797 回答