DevOps & Scaling

AWS Lambda + CaptchaAI: Sunucusuz CAPTCHA Çözümü

CAPTCHA çözümü için 7/24 açık bir sunucu tutmak pahalıdır. AWS Lambda ile fonksiyon yalnızca çözüm isteği geldiğinde çalışır; boştayken ödeme yapmazsınız. CaptchaAI'nin HTTP API'siyle birleşince sonuç kendi kendine ölçeklenen bir çözüm hattı olur: istek alın, CaptchaAI'ye gönderin, token'ı döndürün. Bu rehber tek bir Lambda fonksiyonunu handler'dan Secrets Manager ve SQS'e kadar sıfırdan yayına alır.


Mimari: istekten token'a akış

Kurulumun tamamı üç parçadan oluşur ve her biri diğerlerinden bağımsız ölçeklenir:

  • API Gateway veya SQS gelen isteği karşılar ve Lambda'yı tetikler.
  • Lambda fonksiyonu görevi CaptchaAI'ye gönderir, sonucu sorgular ve token'ı döndürür.
  • Secrets Manager API anahtarınızı kodun dışında, şifreli tutar.

Bu ayrım sayesinde trafik arttığında yalnızca Lambda eşzamanlılığı büyür; yönetmeniz gereken bir sunucu ya da ölçek grubu kalmaz. Aşağıdaki bölümlerde bu üç parçayı sırayla kuracağız.


Lambda handler'ı: gönder ve sorgula

Handler görevi CaptchaAI'ye gönderir (in.php) ve sonucu res.php üzerinden sorgular; urllib yerleşik olduğu için ekstra bağımlılık ya da Lambda katmanı gerekmez. Çözme mantığını ayrı bir solve_captcha fonksiyonuna taşımak, aynı kodu hem API Gateway hem de SQS tetikleyicisinde yeniden kullanmanızı sağlar.

# lambda_function.py
import json
import os
import time
import urllib.request
import urllib.parse


def lambda_handler(event, context):
    """AWS Lambda handler for CaptchaAI solving."""
    api_key = os.environ["CAPTCHAAI_KEY"]

    # Parse input
    body = json.loads(event.get("body", "{}")) if isinstance(event.get("body"), str) else event

    method = body.get("method", "userrecaptcha")
    params = body.get("params", {})

    try:
        token = solve_captcha(api_key, method, params)
        return {
            "statusCode": 200,
            "body": json.dumps({"token": token}),
        }
    except Exception as e:
        return {
            "statusCode": 500,
            "body": json.dumps({"error": str(e)}),
        }


def solve_captcha(api_key, method, params, timeout=90):
    """Solve CAPTCHA using CaptchaAI API."""
    # Submit task
    submit_data = urllib.parse.urlencode({
        "key": api_key,
        "method": method,
        "json": 1,
        **params,
    }).encode()

    req = urllib.request.Request(
        "https://ocr.captchaai.com/in.php",
        data=submit_data,
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        result = json.loads(resp.read())

    if result.get("status") != 1:
        raise RuntimeError(f"Submit error: {result.get('request')}")

    task_id = result["request"]

    # Poll for result
    start = time.time()
    while time.time() - start < timeout:
        time.sleep(5)
        poll_url = (
            f"https://ocr.captchaai.com/res.php"
            f"?key={api_key}&action=get&id={task_id}&json=1"
        )
        with urllib.request.urlopen(poll_url, timeout=15) as resp:
            data = json.loads(resp.read())

        if data["request"] != "CAPCHA_NOT_READY":
            if data.get("status") == 1:
                return data["request"]
            raise RuntimeError(f"Solve error: {data['request']}")

    raise TimeoutError("Solve timeout")

Fonksiyon iki aşamalı çalışır: önce görevi gönderip bir task_id alır, ardından res.php uç noktasını beş saniyede bir sorgulayarak CAPCHA_NOT_READY yanıtı bitene kadar bekler. Zaman aşımı süresi dolarsa TimeoutError fırlatır, böylece fonksiyon takılı kalmaz.


API anahtarını Secrets Manager'da saklayın

API anahtarını koda ya da ortam değişkenine gömmek yerine AWS Secrets Manager'da tutun; fonksiyon anahtarı çalışma anında okur ve değer log'lara sızmaz. Bu, KVKK kapsamındaki denetimlerde beklenen sır yönetimi pratiğidir.

import json
import boto3


def get_api_key():
    """Retrieve CaptchaAI key from AWS Secrets Manager."""
    client = boto3.client("secretsmanager")
    response = client.get_secret_value(SecretId="captchaai/api-key")
    secret = json.loads(response["SecretString"])
    return secret["api_key"]

Sırrı bir kez oluşturun:

aws secretsmanager create-secret \
  --name captchaai/api-key \
  --secret-string '{"api_key":"YOUR_API_KEY"}'

Fonksiyona yalnızca bu tek sırrı okuma izni verin; geniş secretsmanager:* izinleri yerine kaynak ARN'sini daraltmak en az ayrıcalık ilkesine uyar.


SAM şablonu: altyapıyı kod olarak tanımlayın

SAM şablonu fonksiyonu, /solve uç noktasını ve Secrets Manager iznini tek dosyada tanımlar. Altyapıyı kod olarak tutmak, aynı yığını farklı bölgelerde ya da staging ortamında tekrarlanabilir biçimde kurmanızı sağlar.

# template.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 120
    MemorySize: 256
    Runtime: python3.11

Resources:
  CaptchaSolverFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: lambda_function.lambda_handler
      Environment:
        Variables:
          CAPTCHAAI_KEY: !Sub "{{resolve:secretsmanager:captchaai/api-key:SecretString:api_key}}"
      Events:
        SolveApi:
          Type: Api
          Properties:
            Path: /solve
            Method: post
      Policies:

        - AWSSecretsManagerGetSecretValuePolicy:
            SecretArn: !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:captchaai/api-key-*"

Outputs:
  SolveApiUrl:
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/solve"

Fonksiyonu yayına alın

İki komutla derleyip yayına alın; --guided bayrağı ilk dağıtımda bölgeyi ve yığın adını sorar, sonraki dağıtımlarda kaydedilen ayarları kullanır.

# Build and deploy
sam build
sam deploy --guided

# Test
curl -X POST https://YOUR_API_ID.execute-api.us-east-1.amazonaws.com/Prod/solve \
  -H "Content-Type: application/json" \
  -d '{
    "method": "userrecaptcha",
    "params": {
      "googlekey": "SITE_KEY",
      "pageurl": "https://example.com"
    }
  }'

Dönen SolveApiUrl çıktısını not edin; entegrasyon testlerinizde bu uç noktaya POST isteği göndereceksiniz.


SQS ile toplu CAPTCHA işleme

Yüksek hacimli işlerde istekleri doğrudan API Gateway yerine bir SQS kuyruğuna yazın. Lambda kuyruğu toplu tüketir ve ani yükleri tamponlar; böylece bir trafik sıçraması eşzamanlılık limitine çarpıp istekleri düşürmez. Türk e-ticaret ekiplerinin kampanya dönemlerinde checkout akışlarını test ederken gördüğü hacim dalgalanmaları için bu desen özellikle uygundur.

import json
import os
import time
import urllib.request
import urllib.parse


def sqs_handler(event, context):
    """Process CAPTCHA tasks from SQS queue."""
    api_key = os.environ["CAPTCHAAI_KEY"]
    results = []

    for record in event["Records"]:
        task = json.loads(record["body"])
        try:
            token = solve_captcha(
                api_key,
                task["method"],
                task["params"],
            )
            results.append({
                "task_id": task.get("id"),
                "status": "success",
                "token": token[:50],
            })
        except Exception as e:
            results.append({
                "task_id": task.get("id"),
                "status": "error",
                "error": str(e),
            })

    return {"results": results}

Kuyruk ayrıca yeniden deneme ve ölü mektup kuyruğu (dead-letter queue) davranışını üstlenir: çözülemeyen görevler kaybolmaz, incelemek üzere ayrı bir kuyruğa taşınır.


Lambda yapılandırmasında dikkat edilecekler

Fonksiyonu üretime almadan önce aşağıdaki ayarları çözüm sürenize göre gözden geçirin:

Faktör Değer
Maksimum zaman aşımı 15 dakika (çoğu CAPTCHA için 2 dakikaya ayarlanmıştır)
Bellek 256 MB yeterli (yoğun işlem yok)
Eşzamanlılık Varsayılan 1000 eşzamanlı (gerekirse artış isteyin)
Soğuk başlatma Python için ~500ms (çözüm süresine karşı ihmal edilebilir)
Maliyet ~Çözüm başına 0,0001$ (yalnızca hesaplama)
Bağımlılıklar Lambda katmanlarından tanılamak için urllib (yerleşik) kullanın

Sorun giderme

En sık karşılaşılan dört sorun ve hızlı çözümleri:

Sorun Sebep Düzeltme
İşlev zaman aşımına uğradı Lambda zaman aşımı < çözüm süresi Zaman aşımını 120 saniyeden fazla olarak ayarlayın
İzin gizli olarak reddedildi Eksik IAM politikası SecretsManager okuma politikasını ekleyin
Soğuk başlatma gecikmeyi artırır Seyrek çağrılar Sağlanan eşzamanlılığı kullan
İstekler için içe aktarma hatası Lambda'da paketlenmemiştir urllib.request (yerleşik) kullanın veya katman ekleyin

Maliyet: sunucusuz gerçekten ucuz mu?

Sunucusuz modelin cazibesi boşta maliyetin sıfır olmasıdır. Çözüm başına Lambda hesaplama ücreti yaklaşık 0,0001$ seviyesindedir; asıl gider CaptchaAI thread planınızdır. Sabit bir worker'ı 7/24 açık tutmak yerine yalnızca gelen isteği ödersiniz, bu da değişken ve düşük hacimli iş yüklerinde belirgin bir tasarruf sağlar.

Eşzamanlı çözüm kapasitesi Lambda tarafında değil, plan thread sayınızda belirlenir. Düşük hacim için BASIC ($15/ay, 5 thread), yoğun kampanya yükleri için ADVANCE ($90/ay, 50 thread) veya PREMIUM ($170/ay, 100 thread) uygundur. Fiyatlar USD cinsindendir; TL kuru oynadıkça öngörülebilir aylık USD gideri, sunucusuz kurulumun ek bir avantajıdır.


Sık sorulan sorular

Sunucusuz Lambda mı, sürekli çalışan bir worker mı seçmeliyim?

Değişken veya düşük hacimli iş yüklerinde Lambda daha ekonomiktir; yalnızca çözüm yaparken ödeme yaparsınız. Sabit ve çok yüksek hacimli akışlarda sürekli çalışan bir worker daha öngörülebilir olabilir.

Aynı anda çok sayıda CAPTCHA çözersem hangi plana ihtiyacım olur?

Eşzamanlılık Lambda tarafında değil, CaptchaAI thread sayısında sınırlanır. Düşük hacim için BASIC ($15/ay, 5 thread), yoğun yükler için ADVANCE ($90/ay, 50 thread) veya PREMIUM ($170/ay, 100 thread) uygundur. Fiyatlar USD'dir ve thread başınadır.

Lambda zaman aşımını kaç saniyeye ayarlamalıyım?

Çoğu CAPTCHA 10–60 saniyede çözülür. Zaman aşımını 120 saniyeye ayarlayın; reCAPTCHA Enterprise gibi ağır türlerde 180 saniyeye çıkarın. Zaman aşımı çözüm süresinden kısa kalırsa fonksiyon erken sonlanır.

API anahtarımı ortam değişkeninde tutmak güvenli mi?

Kısa testlerde yeterli olsa da üretimde anahtarı Secrets Manager'da tutun ve erişimi IAM ile sınırlayın; böylece anahtar log'lara sızmaz.


İlgili rehberler


Sunucusuz mimariye geçin — CaptchaAI anahtarınızı alın ve ilk çözümünüzü bugün çalıştırın.

Bu makale için yorumlar devre dışı bırakılmıştır.