从 asyncio 使用同步 Python SDK

作者:solvethree阅读需 1 分钟

使用 asyncio.to_thread 运行同步的 Solvethree.solve 方法,避免阻塞事件循环。用信号量限制线程卸载操作,并在每次工作线程调用内部创建独立客户端。SDK 没有原生异步客户端;请求和轮询仍在线程中同步运行。

本页内容

限制并发并设置 SDK 截止时间

此示例需要 Python 3.10+ 和官方 Python SDK。在服务器环境中设置 SOLVETHREE_API_KEY 和 SOLVETHREE_PROXY。将 accounts.example.com 和 login 替换为目标主机名和操作。该站点必须已有工作区可用的有效配置;请参阅站点配置设置。

asyncio · 限制同步 SDK 工作量
import asyncio
import os
import uuid
from solvethree import Solvethree

async def main():
    limit = asyncio.Semaphore(4)
    async def solve_one(number):
        task = {"site": "accounts.example.com", "action": "login", "proxy": os.environ["SOLVETHREE_PROXY"], "requestId": str(uuid.uuid4())}
        def blocking_solve():
            return Solvethree(os.environ["SOLVETHREE_API_KEY"]).solve(task, timeout=180)
        async with limit:
            try:
                result = await asyncio.to_thread(blocking_solve)
                print("Ready task:", result["id"])
                # Submit the solution now with its proxy and browser identity.
            except Exception as error:
                print("Job", number, "requestId", task["requestId"], "error class", type(error).__name__)
                # Retain task and inspect SDK recovery IDs before restarting it.
    await asyncio.gather(*(solve_one(number) for number in range(8)))

asyncio.run(main())
# Canceling an await does not stop an already-running thread or remote task.
  • 提交给 gather 的八个作业中,信号量每次允许四个作业执行。等待空位的作业尚未开始调用 SDK。
  • 每个工作线程都会构造自己的 Solvethree 客户端,而不是在线程之间共享客户端。
  • SDK 的 timeout=180 设置求解截止时间,不包括等待信号量的时间。在 await 外层设置 asyncio 超时,不能替代 SDK 的这一截止时间。
  • 任务及其 requestId 在线程开始执行前创建。保留两者以供恢复,不要每次发生错误时都生成新的 ID。

SDK 只创建一次任务,然后持续轮询,直到任务就绪或抛出错误。无需在 solve 外再编写一层轮询循环。

取消不会停止工作线程或远程任务

取消正在等待 to_thread 的协程,不会停止已运行的线程,也不会取消远程求解。示例中的普通异常处理并不是取消清理逻辑。

这也会影响并发限制:取消可能释放信号量,但工作线程仍在继续运行。如果立即允许替代作业执行,活跃的 SDK 调用可能超过四个。如果取消期间也需要严格限制并发,请跟踪尚未完成的工作线程,并在它们结束前保持其容量占用。

用于生产环境时,请将完全一致的任务输入和请求 ID 保存在协程退出后恢复流程仍可访问的位置。超时意味着你停止接收结果,并不一定意味着任务创建失败。

  1. 1.

    恢复已知任务

    检查 Python SDK 错误中的 task_id 和 request_id。如果已有任务 ID,请通过 get_task 继续获取任务,而不是创建另一个任务。

  2. 2.

    恢复中断的创建请求

    如果没有任务 ID,请使用原始 requestId 和完全相同的任务输入重放请求。如果创建已成功,这会返回现有任务。复用该 ID 时更改输入会产生冲突。

  3. 3.

    区分失败与中断

    TaskFailedError 表示任务已进入失败终态。检查其 errorCode 和 error;不要继续轮询,也不要盲目重试未更改的无效输入。HTTP 错误属于另一个故障层面。

工作线程返回后提交解决结果

示例会打印就绪任务的 ID,但不会向目标提交任何内容。请将该打印语句后的注释替换为应用特定的提交逻辑,并及时使用解决结果,不要等到 gather 中的所有作业都完成。

提交返回的解决结果时,同时使用其 userAgent、secChUa 以及求解时使用的同一代理。SDK 的 successful_proxy 辅助函数会根据原始任务和就绪结果选择代理。就绪结果表示已生成解决结果,不代表应用请求已成功。

运行您的第一个任务。

使用 $5 工作区额度开始体验。

创建工作区
从 asyncio 使用同步 Python SDK · solvethree