处理 Go SDK 错误并恢复中断的任务

作者:solvethree阅读需 1 分钟

超时并不意味着任务创建失败。请保留原始任务输入和 SDK 的请求 ID。如果持有任务 ID,请使用 GetTask 恢复;否则,使用完全相同的原始输入和相同的请求 ID 重放 CreateTask。

取消操作会让 Go 客户端停止等待,而不会停止远程任务。请先恢复该任务,再付费进行新的求解。

本页内容

运行错误处理示例

使用 Go 1.22 或更高版本,以及版本为 v0.1.0 的官方 solvethree.com/sdk 模块。设置方法请参阅 Go 集成说明。

设置服务器端环境变量 SOLVETHREE_API_KEY 和 SOLVETHREE_PROXY。将示例中的 Site 和 Action 替换为目标主机名和操作。该站点需要在工作区中有有效配置;发送其主机名不会创建配置。此 reCAPTCHA 请求使用单个代理,不需要 Type 或 URL。

Go · 检查错误并保留恢复 ID
package main

import (
    "context"
    "fmt"
    "errors"
    "log"
    "os"
    "time"
    solvethree "solvethree.com/sdk"
)

func main() {
    client, err := solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"), nil)
    if err != nil { log.Fatal(err) }
    ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
    defer cancel()
    input := solvethree.TaskRequest{Site: "accounts.example.com", Action: "login", Proxy: os.Getenv("SOLVETHREE_PROXY")}
    result, err := client.Solve(ctx, input, solvethree.SolveOptions{})
    if err != nil {
        var failed *solvethree.TaskFailedError
        var api *solvethree.APIError
        var recovery *solvethree.Error
        switch {
        case errors.As(err, &failed):
            log.Printf("Failed task %s: %s", failed.TaskID, failed.ErrorCode)
        case errors.As(err, &recovery):
            log.Printf("Recovery request=%s task=%s", recovery.RequestID, recovery.TaskID)
            if errors.As(err, &api) { log.Printf("HTTP %d; retry delay %s", api.StatusCode, api.RetryAfter) }
            // Save input plus recovery.RequestID. Resume GetTask for a known TaskID;
            // otherwise replay CreateTask with identical inputs and that RequestID.
        }
        return
    }
    fmt.Println("Ready task:", result.ID)
    // Submit result.Solution.Token with its browser identity and original proxy.
}

此示例将上下文截止时间设为 180 秒,并在 SDK 错误提供恢复所需的 ID 时记录它们。记录日志并不等于持久化恢复信息:在丢弃错误之前,请保存原始 input、返回的 RequestID 以及任何 TaskID。

“任务已就绪”表示解决结果可用,而不是目标站点已接受它。SDK 不会替你提交。请使用返回的浏览器身份信息提交 Solution.Token;在这个单代理示例中,还应使用原始代理。

区分任务失败与请求中断

使用 errors.As 检查包装的错误,使用 errors.Is 检查取消状态。这些检查回答的是不同的问题:一个错误可能包含恢复所需的 ID、包装 HTTP 错误,同时仍需要按操作中断来处理。

各类错误提供的信息
各类错误提供的信息
检查项含义与后续操作
使用 errors.As 检查 *solvethree.TaskFailedError任务已进入终止性失败状态。读取 TaskID、RequestID 和 ErrorCode。使用 GetTask 获取失败的任务以读取其 Error 文本,然后停止轮询该任务。不要盲目重新提交未修改的输入。
使用 errors.Is 检查 context.DeadlineExceeded 或 context.Canceled调用方已停止等待。这并不能确定创建是否成功,或远程任务是否已完成。请保留恢复信息,并在适当时使用新的上下文恢复。
使用 errors.As 检查 *solvethree.ErrorSDK 包装错误会提供 RequestID 和 TaskID,并包装底层的 Cause。请将这些 ID 与完全一致的原始输入一起保存。
使用 errors.As 检查 *solvethree.APIError检查 HTTP StatusCode 和 RetryAfter 时长。HTTP 错误与状态为 failed 的任务不同;不要假定已收到成功的任务响应数据。

此示例在恢复分支中检查嵌套的 APIError。如果应用需要区分截止时间到期和显式取消,请另行使用 errors.Is 检查取消状态。

Solve 只创建一次任务。它仅对 HTTP 429、502、503 或 504 这类临时轮询响应进行重试,并在上下文截止时间内遵循 Retry-After。CreateTask 绝不会自动重试其 POST 请求。

恢复现有任务

  1. 1.

    完整保留原始请求

    将所有任务输入(包括代理或候选列表)与请求 ID 一起保存。如果省略请求 ID,CreateTask 会生成一个;请捕获恢复错误中提供的生成值。不要事后根据可能已发生变化的配置重新构造请求。

  2. 2.

    已知任务 ID 时使用 GetTask

    如果旧上下文已过期或被取消,请创建新的上下文,然后使用 GetTask 获取已保存的 TaskID 对应的任务。状态为 pending 或 solving 时继续等待,仅在 ready 时使用解决结果,在 failed 时停止。

  3. 3.

    仅在不知道任务 ID 时重放创建请求

    将已保存的原始 TaskRequest 中的 RequestID 设为恢复得到的请求 ID,然后使用相同的输入调用 CreateTask。复用相同的 ID 和请求会返回现有任务。复用该 ID 时更改输入会导致冲突。

不要仅因连接超时就使用新的请求 ID。这样会启动一个新的逻辑任务,而不是恢复状态不明的原任务。请先确定原任务发生了什么;如果它已失败,先检查其错误,再决定是否需要修正请求。

运行您的第一个任务。

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

创建工作区
处理 Go SDK 错误并恢复中断的任务 · solvethree