Utiliser le SDK Python synchrone depuis asyncio

Par solvethree4 min de lecture

Utilisez asyncio.to_thread pour exécuter la méthode synchrone Solvethree.solve sans bloquer votre boucle d’événements. Encadrez cette exécution déportée par un sémaphore et créez un client distinct dans chaque appel du thread de travail. Le SDK ne possède pas de client asynchrone natif ; ses requêtes et ses interrogations s’exécutent toujours de manière synchrone dans le thread.

Sur cette page

Limiter les traitements et définir le délai du SDK

L’exemple nécessite Python 3.10+ et le SDK Python officiel. Définissez SOLVETHREE_API_KEY et SOLVETHREE_PROXY dans l’environnement de votre serveur. Remplacez accounts.example.com et login par le nom d’hôte et l’action de votre cible. Le site doit déjà disposer d’un profil actif accessible à votre espace de travail ; consultez la configuration du profil de site.

asyncio · limiter le travail du SDK synchrone
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.
  • Le sémaphore autorise quatre traitements à la fois parmi les huit soumis à gather. Les traitements qui attendent une place n’ont pas encore lancé leur appel au SDK.
  • Chaque thread de travail construit son propre client Solvethree au lieu d’en partager un entre les threads.
  • Le paramètre timeout=180 du SDK définit le délai de résolution. Le temps passé à attendre le sémaphore n’est pas inclus. Un délai asyncio appliqué autour de l’attente ne remplace pas ce délai du SDK.
  • La tâche et son requestId sont créés avant l’entrée dans le thread de travail. Conservez les deux pour la reprise au lieu de générer un nouvel identifiant à chaque erreur.

Le SDK crée la tâche une seule fois et l’interroge jusqu’à ce qu’elle soit prête ou qu’une erreur soit levée. Vous n’avez pas besoin d’écrire une seconde boucle d’interrogation autour de solve.

L’annulation n’arrête ni le thread de travail ni la tâche distante

Annuler la coroutine qui attend to_thread n’arrête pas un thread déjà en cours d’exécution et n’annule pas la résolution distante. Le gestionnaire d’exceptions ordinaire de l’exemple n’effectue pas le nettoyage lié à l’annulation.

Cela affecte aussi la limite de concurrence : l’annulation peut libérer le sémaphore alors que le thread de travail continue. Si vous autorisez immédiatement des traitements de remplacement, plus de quatre appels au SDK peuvent rester actifs. Si vous avez besoin d’une limite stricte pendant l’annulation, suivez les threads de travail en cours et gardez leur capacité occupée jusqu’à leur terminaison.

En production, conservez les données d’entrée exactes de la tâche et l’identifiant de requête à un emplacement auquel votre mécanisme de reprise pourra accéder après la fin de la coroutine. L’expiration du délai signifie que vous avez cessé de recevoir un résultat, pas nécessairement que la création de la tâche a échoué.

  1. 1.

    Reprendre une tâche connue

    Examinez les champs task_id et request_id de l’erreur du SDK Python. Si vous disposez d’un identifiant de tâche, reprenez la récupération avec get_task au lieu de créer une autre tâche.

  2. 2.

    Reprendre une création interrompue

    Si vous ne disposez pas d’un identifiant de tâche, renvoyez les mêmes données d’entrée avec le requestId d’origine. La tâche existante est alors renvoyée si la création a réussi. Modifier les données d’entrée tout en réutilisant cet identifiant provoque un conflit.

  3. 3.

    Distinguer l’échec de l’interruption

    Une erreur TaskFailedError signifie que la tâche a atteint un état d’échec définitif. Examinez ses champs errorCode et error ; ne continuez pas à l’interroger et ne renvoyez pas aveuglément des données d’entrée invalides inchangées. Les erreurs HTTP constituent un niveau d’échec distinct.

Envoyer la solution dès le retour du thread de travail

L’exemple affiche l’identifiant de la tâche prête ; il n’envoie rien à la cible. Remplacez le commentaire après cet affichage par l’envoi propre à votre application et utilisez rapidement la solution au lieu d’attendre la fin de tous les traitements de gather.

Envoyez la solution renvoyée avec ses champs userAgent et secChUa, ainsi qu’avec le même proxy que celui utilisé pour la résolution. La fonction utilitaire successful_proxy du SDK sélectionne le proxy à partir de la tâche d’origine et du résultat prêt. Un résultat prêt signifie qu’une solution a été générée, pas que la requête de votre application a réussi.

Lancez votre première tâche.

Démarrez avec $5 de crédit dans votre espace de travail.

Créer un espace de travail
Utiliser le SDK Python synchrone depuis asyncio · solvethree