So lösen Sie Captchas in einem Hangfire-Hintergrundjob (.NET)

Um ein Captcha in einem Hangfire-Hintergrundjob zu lösen, rufen Sie den .NET-Client von CapSkip innerhalb der Job-Methode auf, senden den Token in demselben Job ab und übergeben den CancellationToken von Hangfire an den Lösevorgang. Dieser Teil braucht zehn Zeilen. Die eigentliche Arbeit steckt in zwei Standardwerten von Hangfire, die zu gewöhnlichen Jobs passen, aber nicht zu einem Hangfire-Captcha-Job: zehn automatische Retries bei jeder Exception, verteilt über etwa viereinhalb Stunden, und ein Pool von bis zu zwanzig Workern, in dem auch ein asynchroner Lösevorgang seinen Worker von Anfang bis Ende belegt. Dieser Leitfaden behandelt den Job, eine Retry-Regel, die weiß, welche Fehler eine Wiederholung wert sind, eine auf CapSkip abgestimmte Queue, das Herunterfahren und das Hosting von Hangfire auf demselben Windows-Rechner wie der Solver.
Was Sie brauchen
- CapSkip auf einem Windows-Rechner. Hangfire kann auf demselben Rechner laufen oder CapSkip von einem anderen Rechner aus aufrufen.
- Hangfire 1.8 mit beliebigem Storage. Die Beispiele verwenden SQL Server als Storage, die übliche Wahl unter Windows, und der Retry-Filter unten braucht 1.8.0 oder neuer.
- Das NuGet-Paket von CapSkip. Jede Lösemethode nimmt einen optionalen CancellationToken an, und genau damit kann ein Herunterfahren einen Lösevorgang sauber stoppen.
- .NET 8 oder neuer. Die Beispiele verwenden primäre Konstruktoren, die mit C# 12 eingeführt wurden.
- Eine Adresse für den Solver. Der Local-Modus antwortet auf 127.0.0.1 nur für dieses Gerät; der Server-Modus lauscht auf Ihrer Netzwerkadresse oder öffentlichen IP, damit Hangfire auf einem anderen Rechner ihn über die API aufrufen kann. Beide finden Sie unter Verbindungseinstellungen.
# dotnet add package CapSkip dotnet add package CapSkip dotnet add package Hangfire.NetCore dotnet add package Hangfire.SqlServer dotnet add package Microsoft.Data.SqlClient
Hangfire.NetCore bringt Hangfire.Core und die Registrierungsmethoden AddHangfire und AddHangfireServer mit. Eine Web-App, die auch das Dashboard haben soll, fügt stattdessen Hangfire.AspNetCore hinzu. Hangfire.SqlServer 1.8 wird ohne eigenen SQL-Client ausgeliefert, deshalb steht Microsoft.Data.SqlClient auf der Liste.
Schritt 1: Lösen und Absenden in einem Job
Packen Sie den Lösevorgang und das Absenden des Formulars in eine einzige Job-Methode; jeder Hangfire-Captcha-Job in diesem Leitfaden baut auf dieser Regel auf. Ein reCAPTCHA-Token ist etwa zwei Minuten gültig, das Absenden kann also nicht in einer Queue hinter anderer Arbeit warten, und genau das würde eine mit ContinueJobWith erstellte Continuation tun. Dasselbe gilt für einen Retry: Jeder Durchlauf muss neu lösen und darf nie bei einem Token oder einer Captcha-id weitermachen, die ein früherer Versuch gespeichert hat.
// dotnet add package CapSkip
using CapSkip;
public class SignupJob(CapSkipClient solver, HttpClient http)
{
public async Task RunAsync(string pageUrl, string sitekey, CancellationToken ct)
{
// Solve and post together: the token lasts about 2 minutes.
var result = await solver.RecaptchaAsync(sitekey, pageUrl, cancellationToken: ct);
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
["g-recaptcha-response"] = result.Code,
});
// No ct here: once the submit starts, let it finish.
var response = await http.PostAsync(pageUrl, form);
response.EnsureSuccessStatusCode(); // a rejected post fails the job
}
}Stellen Sie den Job mit einfachen Werten in die Queue. Hangfire serialisiert die Argumente in den Storage, übergeben Sie also Strings, niemals den Client, und übergeben Sie CancellationToken.None als Platzhalter. Hangfire setzt kurz vor dem Start des Jobs den echten Token ein.
BackgroundJob.Enqueue<SignupJob>(job =>
job.RunAsync("https://example.com/signup", "YOUR_SITEKEY", CancellationToken.None));Hangfire erzeugt SignupJob aus Ihrem Service-Container, registrieren Sie den CapSkip-Client also als Singleton und geben Sie dem Job einen typisierten HttpClient, wie es das vollständige Beispiel tut. Der Client enthält nichts außer seinen Einstellungen, eine Instanz kann also gefahrlos von allen Workern gemeinsam genutzt werden. Die Formularfelder hier sind nur Beispiele; senden Sie das, was das Zielformular tatsächlich sendet.
Schritt 2: Die Standard-Retry-Regel ersetzen
Hangfire wendet auf jeden Job einen automatischen Retry-Filter an. Standardmäßig wiederholt er bei jeder Exception zehnmal, und die Verzögerung vor Retry n beträgt (n minus 1) hoch vier Sekunden, plus 15, plus ein zufälliger Anteil, was sich zwischen dem ersten und dem letzten Fehlschlag auf rund viereinhalb Stunden summiert. Das passt zu einem unzuverlässigen Mailserver. Es passt nicht zu einem Captcha-Job, bei dem manche Fehler einen weiteren Versuch wert sind und andere jedes Mal genau gleich scheitern.
| Was das SDK wirft | Übliche Ursache | Lohnt ein Retry? |
|---|---|---|
| CapSkip.TimeoutException | Der Lösevorgang dauerte länger als recaptchaTimeout (standardmäßig 300 Sekunden), oder CapSkip war nicht erreichbar oder wurde neu gestartet, während das SDK pollte | Ja |
| NetworkException | CapSkip war nicht erreichbar, als der Job die Aufgabe übermittelte | Ja |
| ApiException mit ERROR_CAPTCHA_UNSOLVABLE | Dieser Versuch ist gescheitert oder lief in CapSkip in ein Zeitlimit; der nächste gelingt vielleicht | Ja |
| ApiException mit einem anderen Code | Ein fehlerhafter sitekey oder eine fehlerhafte Seiten-URL oder ein API-Schlüssel, den CapSkip ablehnt | Nein, es scheitert jedes Mal auf dieselbe Weise |
| ValidationException | Ihr Code hat eine Option gesendet, die die Methode nicht annimmt | Nein, das ist ein Bug |
Eine Grenze dieser Tabelle: CapSkip kann nur prüfen, ob ein sitekey formal korrekt ist. Ein formal korrekter Schlüssel, den Google ablehnt, erscheint trotzdem als ERROR_CAPTCHA_UNSOLVABLE, er verbraucht also seine Retries, bevor der Job fehlschlägt.
Hangfire 1.8 hat dem Retry-Attribut OnlyOn hinzugefügt, das Retries auf die Exception-Typen beschränkt, die Sie auflisten. ApiException deckt sowohl eine vorübergehende als auch eine dauerhafte Zeile ab, daher wandelt der Job die dauerhafte Art in einen Exception-Typ um, der nicht auf der Liste steht:
[AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 30, 120, 600 },
OnlyOn = new[] { typeof(CapSkip.TimeoutException),
typeof(NetworkException), typeof(ApiException) })]
public async Task RunAsync(string pageUrl, string sitekey, CancellationToken ct)
{
SolveResult result;
try
{
result = await solver.RecaptchaAsync(sitekey, pageUrl, cancellationToken: ct);
}
catch (ApiException ex) when (!ex.Message.Contains("ERROR_CAPTCHA_UNSOLVABLE"))
{
// Not on the OnlyOn list, so Hangfire fails the job at once.
throw new InvalidOperationException($"CapSkip refused the task: {ex.Message}", ex);
}
// ...post the form as in Step 1.
}Attempts zählt die Retries, hier also ein Durchlauf plus bis zu drei weitere. Ein Job, dem die Versuche ausgehen oder der etwas außerhalb der Liste wirft, landet im Zustand Failed und bleibt dort, damit Sie ihn untersuchen können. Schreiben Sie CapSkip.TimeoutException vollständig aus: Mit den impliziten usings eines Projekts ab .NET 6 ist auch System.TimeoutException im Scope, und der Kurzname lässt sich nicht kompilieren. Die Meldung der ApiException ist die rohe Antwort von CapSkip, deshalb funktioniert der Abgleich auf den Fehlercode, und die vollständige Liste der Codes steht in der API-Referenz.
Schritt 3: Lösevorgängen eine eigene Queue und Worker-Anzahl geben
Ein Hangfire-Server betreibt Environment.ProcessorCount mal 5 Worker, begrenzt auf 20. Einen Job async zu machen, gibt keinen Worker frei, während der Lösevorgang wartet: Hangfire führt jeden Job auf seinem Worker-Thread aus und wartet dort, bis der Task abgeschlossen ist. Zwanzig laufende Lösevorgänge bedeuten also zwanzig belegte Worker, solange die Lösevorgänge dauern, und jeder andere Job in der Anwendung, E-Mails zum Zurücksetzen von Passwörtern eingeschlossen, wartet dahinter.
CapSkip hat ein eigenes Limit. Max. Threads für reCAPTCHA steht in den App-Einstellungen standardmäßig auf 10, und Aufgaben darüber hinaus warten in CapSkip auf einen freien Thread. Eine Aufgabe, die länger wartet als das Wait Timeout für reCAPTCHA (standardmäßig 250 Sekunden), wird mit ERROR_CAPTCHA_UNSOLVABLE abgebrochen. Geben Sie jedem Hangfire-Captcha-Job also eine eigene Queue mit genau so vielen Workern, wie CapSkip Threads hat:
// On the job method, next to [AutomaticRetry]:
[Queue("captcha")]
// In Program.cs: one server for solves, sized to CapSkip...
builder.Services.AddHangfireServer(o =>
{
o.Queues = new[] { "captcha" };
o.WorkerCount = 10; // reCAPTCHA Max. Threads in CapSkip
});
// ...and the usual server for everything else.
builder.Services.AddHangfireServer(o => o.Queues = new[] { "default" });Jetzt reiht sich ein Schub von fünfhundert Lösevorgängen im Hangfire-Storage ein, wo Sie ihn sehen können, und dieser Server hat nie mehr als zehn gleichzeitig bei CapSkip laufen. WorkerCount gilt pro Server; wenn Sie den Captcha-Server also in mehr als einem Prozess betreiben, teilen Sie Max. Threads zwischen ihnen auf. Das Attribut wird bei jedem Einreihen des Jobs erneut angewendet, sodass auch Retries in die Queue captcha zurückkehren. Queue-Namen dürfen nur Kleinbuchstaben, Ziffern, Unterstriche und Bindestriche enthalten. Wenn Sie Max. Threads in CapSkip erhöhen, erhöhen Sie WorkerCount entsprechend.
Schritt 4: Herunterfahren und wo Hangfire läuft
Der CancellationToken, den Hangfire übergibt, löst in zwei Fällen aus: Der Server fährt herunter, wegen eines Dienst-Stopps, eines Deployments oder eines IIS-Recyclings, oder der Job wurde im Dashboard gelöscht oder hat dort seinen Zustand geändert, was Hangfire standardmäßig alle fünf Sekunden prüft. Wenn Sie ihn an RecaptchaAsync übergeben, stoppt das Polling sofort. Beim Herunterfahren legt Hangfire den Job dann zurück in seine Queue und führt ihn nach dem Neustart erneut aus, mit einem frischen Lösevorgang. Beim Löschen verwirft Hangfire den Job. CapSkip bringt die verlassene Aufgabe von selbst zu Ende, und niemand holt das Ergebnis ab.
Es gibt einen kleinen Haken. Hangfire stellt den Job nur dann wieder in die Queue, wenn er mit einer OperationCanceledException endet, und genau die löst das SDK aus, während es pollt. Löst der Token aber aus, während das SDK die Aufgabe noch übermittelt (seine erste Anfrage an CapSkip), verpackt das SDK den Abbruch in eine NetworkException. Hangfire behandelt den Durchlauf dann als fehlgeschlagenen Versuch: Mit der Regel aus Schritt 2 plant es nach der nächsten Verzögerung auf der Liste einen Retry ein und verbraucht einen der drei. Eine einzige catch-Klausel vor dem ApiException-Filter macht daraus wieder einen Abbruch:
catch (CapSkipError) when (ct.IsCancellationRequested)
{
// A cancelled submit arrives as NetworkException;
// rethrow as cancellation so Hangfire re-queues the job.
throw new OperationCanceledException(ct);
}Stirbt der Prozess ganz ohne Herunterfahren, übergibt der Storage auf SQL Server den Job an einen anderen Worker, sobald sein Invisibility Timeout abläuft, in Hangfire 1.8 standardmäßig fünf Minuten. So oder so führt Hangfire einen Job mindestens einmal aus, nicht genau einmal, und deshalb lässt das Beispiel ein bereits begonnenes Absenden des Formulars zu Ende laufen, statt es abzubrechen.
Genauso wichtig ist, wo Sie den Hangfire-Server hosten. In einer IIS-Site stoppt der Anwendungspool standardmäßig nach 20 Minuten Leerlauf und wird nach Zeitplan recycelt, und ein gestoppter Pool bedeutet keinen Hangfire-Server: Wiederkehrende Lösevorgänge starten erst, wenn die nächste Webanfrage die Site aufweckt. Setzen Sie entweder Start Mode des Pools auf AlwaysRunning und sein Idle Time-out auf 0 und aktivieren Sie Preload Enabled auf der Site, wofür das IIS-Feature Application Initialization installiert sein muss, oder betreiben Sie den Server in einem Windows Service, wie es das vollständige Beispiel tut. Auf demselben Windows-Rechner wie CapSkip braucht er nur den Local-Modus und 127.0.0.1.
Planen Sie einen Unterschied zwischen beiden ein. Der Dienst startet beim Booten, CapSkip ist dagegen eine Desktop-App, die startet, wenn Sie sich bei Windows anmelden. Nach einem unbeaufsichtigten Neustart scheitert jede Übermittlung einer Aufgabe mit NetworkException, bis sich jemand anmeldet, und jeder Job verbraucht seine Retries in dieser Lücke. Halten Sie den Rechner angemeldet, oder prüfen Sie ihn nach jedem Neustart.
Wenn Hangfire anderswo läuft, etwa auf einem zweiten Server, einem Container-Host oder in einem Azure App Service, schalten Sie CapSkip in den Server-Modus, damit es auf Ihrer Netzwerkadresse oder öffentlichen IP lauscht, und richten Sie den Client dorthin. Verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt, zusammen mit einer Firewall-Regel für die Adressen, die Sie erwarten. Es bleibt Ihre eigene Hardware, und es bleibt ohne Abrechnung pro Lösung. Der Client liest Umgebungsvariablen nicht von selbst, lesen Sie CAPSKIP_HOST also in Ihrem Startcode aus und übergeben Sie den Wert an den Konstruktor.
Vollständiges lauffähiges Beispiel
Ein Worker-Service, der als Windows Service läuft, mit dem Job aus den Schritten oben und einem wiederkehrenden Zeitplan. Das sind alle Befehle, die er braucht:
# dotnet new worker -n CaptchaWorker dotnet new worker -n CaptchaWorker cd CaptchaWorker dotnet add package CapSkip dotnet add package Hangfire.NetCore dotnet add package Hangfire.SqlServer dotnet add package Microsoft.Data.SqlClient dotnet add package Microsoft.Extensions.Hosting dotnet add package Microsoft.Extensions.Hosting.WindowsServices dotnet add package Microsoft.Extensions.Http dotnet add package Newtonsoft.Json
Drei dieser Zeilen brauchen eine Begründung. Microsoft.Data.SqlClient verschlüsselt Verbindungen standardmäßig, ein lokaler SQL Server ohne vertrauenswürdiges Zertifikat braucht also TrustServerCertificate=true im Connection String. Die Zeile Microsoft.Extensions.Hosting hebt die Hosting-Referenz der Vorlage auf die Version an, die das Paket für Windows Services erwartet; ohne sie scheitert der Restore mit einem Fehler wegen eines Paket-Downgrades. Newtonsoft.Json holt die JSON-Abhängigkeit von Hangfire von einer alten Version weg, die NuGet als verwundbar markiert.
// dotnet add package CapSkip
using CapSkip;
using Hangfire;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService();
builder.Services.AddSingleton(new CapSkipClient(
apiKey: Environment.GetEnvironmentVariable("CAPSKIP_API_KEY") ?? "capskip",
host: Environment.GetEnvironmentVariable("CAPSKIP_HOST") ?? "127.0.0.1",
port: 8080));
builder.Services.AddHttpClient<SignupJob>();
builder.Services.AddHangfire(cfg => cfg
.SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
.UseSimpleAssemblyNameTypeSerializer()
.UseRecommendedSerializerSettings()
.UseSqlServerStorage(builder.Configuration.GetConnectionString("Hangfire")));
builder.Services.AddHangfireServer(o =>
{
o.Queues = new[] { "captcha" };
o.WorkerCount = 10; // reCAPTCHA Max. Threads in CapSkip
});
var host = builder.Build();
host.Services.GetRequiredService<IRecurringJobManager>().AddOrUpdate<SignupJob>(
"nightly-signup",
job => job.RunAsync("https://example.com/signup", "YOUR_SITEKEY", CancellationToken.None),
Cron.Daily());
host.Run();
public class SignupJob(CapSkipClient solver, HttpClient http)
{
[Queue("captcha")]
[AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 30, 120, 600 },
OnlyOn = new[] { typeof(CapSkip.TimeoutException),
typeof(NetworkException), typeof(ApiException) })]
public async Task RunAsync(string pageUrl, string sitekey, CancellationToken ct)
{
SolveResult result;
try
{
result = await solver.RecaptchaAsync(sitekey, pageUrl, cancellationToken: ct);
}
catch (CapSkipError) when (ct.IsCancellationRequested)
{
throw new OperationCanceledException(ct);
}
catch (ApiException ex) when (!ex.Message.Contains("ERROR_CAPTCHA_UNSOLVABLE"))
{
throw new InvalidOperationException($"CapSkip refused the task: {ex.Message}", ex);
}
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
["g-recaptcha-response"] = result.Code,
});
var response = await http.PostAsync(pageUrl, form);
response.EnsureSuccessStatusCode();
}
}Dieser Dienst verarbeitet nur die Queue captcha. Ihre Web-App reiht Jobs in denselben Storage auf SQL Server ein und behält ihren eigenen Server für die Queue default, oder Sie fügen hier wie in Schritt 3 einen zweiten Aufruf von AddHangfireServer hinzu. Sobald auch eine Web-App den Job einreiht, verschieben Sie SignupJob aus Program.cs in eine Klassenbibliothek, die beide Projekte referenzieren, denn die Web-App braucht den Typ, um den Job einzureihen. Das Projekt braucht in appsettings.json einen Connection String namens Hangfire. Installieren Sie den Dienst mit sc.exe create oder in PowerShell mit New-Service und verweisen Sie dabei auf die veröffentlichte ausführbare Datei.
Tauschen Sie RecaptchaAsync gegen TurnstileAsync, FriendlyCaptchaAsync oder eine andere Lösemethode aus, und die Form des Jobs bleibt gleich. Zwei Dinge ändern sich mit dem Typ: Richten Sie WorkerCount nach der Einstellung Max. Threads dieses Typs in CapSkip aus, und senden Sie den Token in dem Feld, das dieser Typ verwendet.
Häufige Fehler und was sie bedeuten
| Was Sie sehen | Ursache | Beheben |
|---|---|---|
| Jobs bleiben in Enqueued stehen und starten nie | Die Methode hat [Queue("captcha")], aber kein Server lauscht auf dieser Queue | Fügen Sie einen Server hinzu, dessen Queues captcha enthält |
| Ein fehlerhafter Job wiederholt sich stundenlang | Der Standard-Retry-Filter, zehn Versuche bei jeder Exception | Verwenden Sie AutomaticRetry mit OnlyOn wie in Schritt 2 |
| Ein Retry mit der Meldung The operation was canceled, direkt nach einem Deployment | Der Token hat ausgelöst, während die Aufgabe übermittelt wurde, und das SDK hat den Abbruch verpackt | Fügen Sie den catch-Block für den Abbruch aus Schritt 4 hinzu |
| NetworkException bei jedem Job | CapSkip läuft nicht, etwa nach einem Neustart ohne angemeldeten Benutzer, oder Hangfire läuft auf einem anderen Rechner und CapSkip ist im Local-Modus | Starten Sie CapSkip; läuft Hangfire auf einem anderen Rechner, schalten Sie CapSkip in den Server-Modus und setzen Sie CAPSKIP_HOST |
| ERROR_CAPTCHA_UNSOLVABLE oder CapSkip.TimeoutException unter Last | Mehr laufende Lösevorgänge, als CapSkip Threads hat, daher warten Aufgaben über das Wait Timeout hinaus | Gleichen Sie WorkerCount auf dem Captcha-Server an Max. Threads an |
| Der Job ist erfolgreich, aber die Website lehnt das Absenden ab | Der Token ist vor dem Absenden abgelaufen, oder das Formular braucht weitere Felder | Erledigen Sie Lösen und Absenden in einem Job und bilden Sie das echte Formular mit den DevTools nach |
| Wiederkehrende Lösevorgänge fallen nachts aus | Der IIS-Anwendungspool war im Leerlauf oder wurde gerade recycelt | Setzen Sie AlwaysRunning, oder hosten Sie den Server in einem Windows Service |
| Der Build scheitert an einer mehrdeutigen TimeoutException | CapSkip und System definieren beide diesen Namen | Schreiben Sie CapSkip.TimeoutException vollständig aus |
FAQ
Gibt ein asynchroner Job seinen Hangfire-Worker frei, während das Captcha gelöst wird?
Nein. Hangfire unterstützt asynchrone Job-Methoden, wartet aber auf dem Worker-Thread, der den zurückgegebenen Task gestartet hat, auf dessen Abschluss, sodass der Worker während des gesamten Lösevorgangs belegt bleibt. Deshalb ist bei einem Hangfire-Captcha-Job die Worker-Anzahl seiner Queue die Zahl, die begrenzt, wie viele Lösevorgänge gleichzeitig laufen, und deshalb sollte sie zu den Threads von CapSkip passen.
Kann ich in einem Job lösen und in einer Continuation absenden?
Das können Sie, aber Sie sollten es nicht. Eine Continuation wird wie jeder andere Job in die Queue gestellt, sie wartet also hinter allem, was bereits ansteht, und ein reCAPTCHA-Token bleibt etwa zwei Minuten gültig. Ein Retry des zweiten Jobs würde außerdem denselben abgelaufenen Token erneut senden. Bleibt beides in einem Job, löst jeder Versuch neu und sendet sofort ab. Mehr dazu, wie lange Tokens gültig bleiben, steht im Leitfaden zum Ablauf von reCAPTCHA-Tokens.
Kann Hangfire auf Azure oder in einem Linux-Container einen Solver auf meinem Windows-PC nutzen?
Ja. Die Hangfire-Seite kann überall laufen, wo .NET läuft; nur CapSkip braucht Windows. Schalten Sie CapSkip in den Verbindungseinstellungen in den Server-Modus, setzen Sie CAPSKIP_HOST überall dort, wo Hangfire läuft, auf seine Adresse, und übergeben Sie den Wert an den Client. Lassen Sie die Verbindung in der Windows Firewall zu, und verwenden Sie eine statische öffentliche IP, wenn der Weg über das Internet führt. Gehostete Plattformen bringen eigene Grenzen mit; für eine davon beschreibt sie der Azure-Functions-Leitfaden.
Wie unterscheidet sich das von Celery in Python?
Die wichtigste Regel ist bei beiden gleich: bei jedem Versuch neu lösen und in derselben Arbeitseinheit absenden. Die Fallen unterscheiden sich. Bei Celery stammen sie aus den Zeitlimits und den Einstellungen zur Bestätigung, bei Hangfire aus den Retry-Standardwerten, aus Workern, die asynchroner Code nicht freigibt, und aus der Art, wie ein Abbruch gemeldet wird. Wie es auf der Python-Seite aussieht, zeigt der Celery-Captcha-Leitfaden.
Die Kurzfassung
Erledigen Sie in einem Hangfire-Captcha-Job Lösen und Absenden in einer einzigen Methode, und übergeben Sie den CancellationToken von Hangfire an den Lösevorgang. Ersetzen Sie die Standard-Retry-Regel durch AutomaticRetry mit OnlyOn, und wandeln Sie dauerhafte ApiExceptions in eine Exception um, die nicht auf der Liste steht. Geben Sie Lösevorgängen eine Queue, deren Worker-Anzahl zu den Threads von CapSkip passt, werfen Sie eine abgebrochene Übermittlung erneut als OperationCanceledException, damit Jobs beim Herunterfahren wieder in die Queue kommen, und hosten Sie den Server dort, wo er nicht in den Leerlauf fallen kann: in einem Windows Service neben CapSkip oder an jedem anderen Ort mit dem Server-Modus.
- Alle Captcha-Typen, die das .NET-Paket löst: die C#- und .NET-Solver-Seite.
- Wie die reCAPTCHA-v2-Checkbox gelöst wird: die reCAPTCHA-v2-Solver-Seite.
Ein letzter Punkt zu Retries. Weil der Solver auf Ihrem eigenen Rechner läuft, kostet ein Retry ein paar Sekunden eines Threads statt eines weiteren abgerechneten Lösevorgangs, und so lässt sich eine Captcha-Umgehung nach einem einzelnen Fehlschlag günstig wiederholen. Wovor die Retry-Regel eigentlich schützt, sind die Jobs, die nie gelingen werden, und genau die sollten Sie früh stoppen.
