CAPTCHA-Aufgaben nebenläufig in Go lösen

Von solvethree3 Min. Lesezeit

Verwenden Sie einen gemeinsamen Go-SDK-Client und einen festen Worker-Pool. Geben Sie jedem Job einen eigenen Kontext und übermitteln Sie jede fertige Lösung sofort, statt Tokens für einen späteren Batch zu sammeln.

Auf dieser Seite

Client und Ziel vorbereiten

  • Verwenden Sie Go 1.22 oder neuer und das offizielle Modul solvethree.com/sdk in Version v0.1.0. Details zur Installation finden Sie in der Dokumentation zur Go-Integration.
  • Setzen Sie SOLVETHREE_API_KEY und SOLVETHREE_PROXY in der Serverumgebung. Halten Sie den API-Schlüssel aus dem Browsercode heraus.
  • Ersetzen Sie accounts.example.com und login durch Ihren Zielhostnamen und Ihre Aktion. Der Workspace benötigt ein aktives Profil für diese Website; durch Übermitteln des Hostnamens wird keines erstellt. Siehe Einrichtung des Websiteprofils.

Das Beispiel fordert reCAPTCHA Enterprise an, die Standardeinstellung. Es übergibt einen Proxy und lässt Type und URL bewusst ungesetzt: reCAPTCHA-Anfragen verwenden den Hostnamen der Website und die Aktion, nicht diese Felder.

Arbeit begrenzen, nicht die Anzahl der Clients

Der Client kann sicher nebenläufig verwendet werden. Dieses Beispiel startet vier Worker und übergibt ihnen acht Jobs über einen ungepufferten Kanal. Sind alle Worker beschäftigt, wartet der Producer, statt für jeden Job eine Goroutine zu starten. Vier ist eine beispielhafte Anwendungseinstellung, kein Dienstlimit von solvethree.

Go · Vier Worker und sofortige Verarbeitung jedes Jobs
package main

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

func main() {
    client, err := solvethree.NewClient(os.Getenv("SOLVETHREE_API_KEY"), nil)
    if err != nil { log.Fatal(err) }
    jobs := make(chan int)
    var workers sync.WaitGroup
    for worker := 0; worker < 4; worker++ {
        workers.Add(1)
        go func() {
            defer workers.Done()
            for job := range jobs {
                ctx, cancel := context.WithTimeout(context.Background(), 180*time.Second)
                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 { cancel(); log.Printf("Job %d: %v", job, err); continue }
                fmt.Println("Ready task:", result.ID)
                // Submit this solution now, with its proxy and browser identity.
                cancel()
            }
        }()
    }
    for job := 0; job < 8; job++ { jobs <- job }
    close(jobs)
    workers.Wait()
}

Jeder Worker erstellt für jeden Job einen separaten Kontext mit 180 Sekunden Laufzeit. Solve erstellt die Aufgabe einmal und wartet auf ihr Ergebnis. Da das Beispiel RequestID weglässt, generiert das SDK eine für diese Erstellung. Ein fehlgeschlagener Aufruf wird protokolliert, und continue führt nur diesen Worker zum nächsten Job weiter; der Batch wird dadurch nicht gestoppt.

Führen Sie den Abbruch wie gezeigt innerhalb der Schleife aus, damit jeder Job seine Kontextressourcen zeitnah freigibt. Der Abbruch beendet das lokale Warten; die entfernte Verarbeitung wird dadurch nicht abgebrochen.

Lösung verarbeiten, bevor der nächste Job übernommen wird

Das Beispiel gibt die ID der fertigen Aufgabe aus; es übermittelt nichts an das Ziel. Ersetzen Sie den Kommentar durch Ihre Zielanfrage und melden Sie anschließend das Übermittlungsergebnis dieses Jobs, bevor der Worker einen weiteren Job übernimmt. Sammeln Sie Tokens nicht in einem Slice zur Verwendung nach workers.Wait.

  • Übermitteln Sie result.Solution.Token, nicht result.ID.
  • Verwenden Sie SuccessfulProxy mit der ursprünglichen Aufgabe und dem fertigen Ergebnis, um den erfolgreichen Proxy zu ermitteln. Übermitteln Sie die Anfrage über diesen Proxy mit der zurückgegebenen Browseridentität aus UserAgent und SecChUA.
  • Führen Sie die Übermittlung innerhalb des Jobkontexts vor cancel() aus. Die Frist von 180 Sekunden umfasst dann sowohl das Lösen als auch die Übermittlung; verwenden Sie einen separaten Übermittlungskontext, wenn Sie ein eigenes Zeitbudget benötigen.

Melden Sie das Lösen und die Übermittlung an das Ziel getrennt. Eine fertige Aufgabe liefert eine verwendbare Lösung, aber erst die Antwort des Ziels zeigt, ob es die Anwendungsanfrage akzeptiert hat.

Unterbrochenen Job wiederaufnehmen, ohne den Batch neu zu starten

Für dieses kleine Beispiel genügt es, einen Fehler zu protokollieren. Eine Integration mit Wiederaufnahmemöglichkeit sollte jedoch die exakten Aufgabeneingaben jedes Jobs sowie verfügbare Anfrage- und Aufgaben-IDs aufbewahren. Eine Zeitüberschreitung beweist nicht, dass die Aufgabenerstellung fehlgeschlagen ist.

  • Extrahieren Sie *solvethree.Error, um RequestID und TaskID zu erhalten. Ist die Aufgaben-ID bekannt, setzen Sie den Abruf mit GetTask unter einem neuen Kontext fort.
  • Wenn die Erstellung ungewiss ist und Sie die Anfrage-ID haben, wiederholen Sie die ursprünglichen Eingaben mit derselben RequestID. Dieselbe Anfrage gibt die vorhandene Aufgabe zurück; geänderte Eingaben bei Wiederverwendung der ID verursachen einen Konflikt.
  • Erfassen Sie bei *TaskFailedError den ErrorCode und rufen Sie die fehlgeschlagene Aufgabe ab, wenn Sie ihren Error-Text benötigen. Beenden Sie das Polling dieser Aufgabe und beheben Sie die Ursache, statt dieselben ungültigen Eingaben blind erneut zu übermitteln.

Unterscheiden Sie HTTP-Fehler von endgültigen Aufgabenfehlern. errors.As kann einen verschachtelten *APIError mit StatusCode und RetryAfter extrahieren. Solve wiederholt bereits Polling-Anfragen bei temporären Antworten mit HTTP 429, 502, 503 oder 504 und berücksichtigt dabei Retry-After innerhalb der Kontextfrist. Die Aufgabenerstellung wird niemals automatisch wiederholt.

Führe deinen ersten Task aus.

Starte mit $5 Workspace-Guthaben.

Workspace erstellen
CAPTCHA-Aufgaben nebenläufig in Go lösen · solvethree