AWS Lambda’da Zaman Aşımına Uğramadan CAPTCHA Nasıl Çözülür

AWS Lambda’da bir captcha çözümü iki yerde bozulur ve ikisi de sizin kodunuz değildir. API Gateway, function’ı 29 saniye sonra beklemeyi bırakır; dolayısıyla 40 saniye süren bir reCAPTCHA, function hâlâ çalışırken çağırana 504 döndürür. Lambda sandbox’ının içindeki 127.0.0.1 ise sandbox’ın kendisidir, yani loopback’e yöneltilmiş bir istemci dinleyen hiçbir şey bulamaz. CapSkip sizin sahip olduğunuz bir makinede çalışır ve bu kurulumda o makine asla function’ınızı çalıştıran makine olmaz. Önce adresi düzeltin, sonra çözümü istek yolundan çıkarın.
Neye ihtiyacınız var
- Kontrol ettiğiniz bir Windows makinesinde çalışan CapSkip. CapSkip bir masaüstü uygulamasıdır ve Lambda’nın içinde çalışmaz. Burada function yalnızca istemcidir, fazlası değil.
- Python 3.10 veya daha yeni bir Lambda runtime’ı ve dağıtım paketinde ya da bir layer içinde CapSkip paketi.
- Server modu açık olmalı. Local modu yalnızca o cihaz için 127.0.0.1 üzerinden yanıt verir ve bu, AWS’te çalışan bir function’a hiçbir işe yaramaz. Server modu ise ağ adresinizi veya genel IP’nizi dinler, böylece function aynı API üzerinden ona ulaşabilir. Her iki mod da şurada yer alır: bağlantı ayarları. Statik bir genel IP önerilir ve AWS’in geleceği tek adres için bir güvenlik duvarı kuralı eklenmelidir.
- Function’dan o adrese ulaşmanın bir yolu. İki biçimi 2. adım anlatıyor, çünkü bir VPC’ye bağlı function, bağlı olmayandan farklı davranır.
Tasarıma neden API Gateway’in 29 saniyelik zaman aşımı karar veriyor
AWS Lambda’da bir captcha çözümünün üç sınıra sığması gerekir. Kod yazmadan önce bunları bir kenara not edin, çünkü bir araya geldiklerinde akla ilk gelen tasarımı eleyip atarlar.
| Limit | Değer | Yükseltebilir misiniz |
|---|---|---|
| API Gateway entegrasyon zaman aşımı | Varsayılan 29 saniye | Regional ve özel REST API’lerinde, kota talebiyle. AWS, bu artışın hesap throttle kotanıza mal olabileceği konusunda uyarıyor |
| Lambda function zaman aşımı | Varsayılan 3 saniye, standart bir function için en fazla 900 saniye | Evet, o 15 dakikalık tavana kadar |
| CapSkip yoklama zaman aşımı | reCAPTCHA, Turnstile ve GeeTest için 300 saniye; görüntü ve ALTCHA için 120 saniye | Evet, ikisi de constructor seçeneğidir |
Yani senkron bir API çağrısı yavaş bir reCAPTCHA’yı kaldıramaz. Function’ın buna yeri vardır, önündeki gateway’in yoktur; çözüm hâlâ çalışıp hâlâ faturalanırken çağıran 504 görür.
İnsanların bir sonraki adımda başvurduğu geçici çözüm daha da kötüdür. Erken dönüp çözümü bir arka plan thread’inde bitirmek işe yaramaz, çünkü handler döndükten sonra Lambda yürütme ortamını dondurur. AWS bunu açıkça söylüyor: function sona erdiğinde tamamlanmamış arka plan süreçleri veya geri çağrıları, Lambda ortamı yeniden kullanırsa yeniden devreye girer. Yeniden devreye girer; kaldığı yerden sürüp gitmez. Thread’iniz dakikalar sonra, token’ının süresi çoktan dolmuş bir CAPTCHA için yoklamanın ortasında, kendisiyle hiç ilgisi olmayan bir çağrının içinde uyanır. Hiçbir şey hata vermez. İş yalnızca yanlış yere düşer. AWS yaşam döngüsünü şurada açıklıyor: yürütme ortamı rehberi.
1. Adım: SDK’yı paketleyin ve function’ı yapılandırın
Bir klasöre kurup handler’ınızla birlikte zip’leyin ya da python adlı bir klasöre kurup onu zip’leyerek layer olarak ekleyin. Platformu ve yorumlayıcıyı dizüstünüzün değil function’ın çalıştırdığı sürüme sabitleyin; aksi halde import, cold start sırasında logda işe yarar hiçbir şey bırakmadan başarısız olur. Bu bayraklar olmadan pip, wheel’leri yerel Python’ınıza göre çözümler ve daha yeni bir yorumlayıcı için derlenmiş bir wheel runtime’da yüklenmez.
# pip install capskip pip install capskip --target package/ \ --platform manylinux2014_x86_64 --implementation cp \ --python-version 3.12 --only-binary=:all: cp lambda_function.py package/ cd package && zip -r ../function.zip . > /dev/null && cd .. aws lambda update-function-code \ --function-name solve-captcha --zip-file fileb://function.zip
Sonra zaman aşımını ve bağlantı bilgilerini kodun içinde değil yapılandırma olarak verin, böylece aynı paket hem bir test çözücüsüne hem de bir üretim çözücüsüne karşı çalışır.
# Timeout in seconds, and the Server mode address
aws lambda update-function-configuration \
--function-name solve-captcha \
--timeout 330 \
--environment "Variables={CAPSKIP_HOST=203.0.113.10,CAPSKIP_PORT=8080}"Function zaman aşımını istemcinin kendi yoklama zaman aşımının biraz üstüne kurun, altına değil. Altına kurarsanız çağrıyı önce Lambda öldürür ve CloudWatch’ta size ne olduğunu anlatacak TimeoutException yerine çıplak bir task timeout görürsünüz.
2. Adım: function’a bir NAT gateway ve bir Elastic IP verin
Bir şeyin çalışıp çalışmayacağına karar veren adım budur ve cevap, ağ ayarı olarak hiç düşünmemiş olabileceğiniz tek bir ayara bağlıdır.
| Function yapılandırması | Nereye ulaşabilir | Güvenlik duvarınızda neye izin verirsiniz |
|---|---|---|
| VPC’ye bağlı değil | Doğrudan genel internete | İşe yarar hiçbir şeye. Çıkış trafiği, AWS’e ait ve değişen adreslerden gelir, dolayısıyla tek bir IP izin listesine alınamaz |
| VPC’ye bağlı, NAT gateway yok | Yalnızca o VPC’nin içindekilere. Çözücünüz orada değil | Hiçbir şeye. Bağlantı reddedilmez, zaman aşımına uğrar |
| VPC’ye bağlı, bir NAT gateway üzerinden yönlendiriliyor | Tek bir adresten genel internete | NAT gateway’in Elastic IP’si; istediğiniz biçim de budur |
Lambda ilk iki satırı doğrudan belgeliyor: function’lar varsayılan olarak genel internete erişir ve birini bir VPC’ye bağladığınızda, function’ın subnet’lerinin dışarı çıkan bir rotası olana kadar erişimi o VPC’nin içindeki kaynaklarla sınırlanır. O rota, genel bir subnet’te duran bir NAT gateway’dir ve şurada anlatılıyor: Lambda internet erişimi rehberi. İşe yarayan kısmı yan etkisidir. Bir NAT gateway bir Elastic IP tutar, dolayısıyla her çözüm makinenize tek ve sabit bir adresten ulaşır ve güvenlik duvarı kuralınız tek satır olabilir.
Function’ı genel subnet’e değil özel subnet’lere bağlayın. NAT gateway zaten yerindeyken takılmaya yol açan tuzak budur: genel bir subnet’e bağlı bir function’ın, yönlendirme tablosu ne derse desin internet erişimi olmaz, dolayısıyla paketler hiçbir yere gitmez. Aynı rehber bunu iki kez tekrarlıyor.
Bir NAT gateway, size tek ve sabit bir kaynak adres veren en basit biçimdir; tek biçim değildir. Aynı VPC’den kurulan bir Site-to-Site VPN veya Direct Connect, kendi ağınızdaki bir çözücüye portunu internete hiç açmadan ulaşır ve makine, bir port açmayı tercih etmeyeceğiniz bir yerdeyse ikisi de kurulum zahmetine değer. Her iki durumda da çözücünün portunu diğer her şeye kapalı tutun. Server modu yine sizin donanımınızdır ve çözüm başına ücretlendirme yine yoktur: yalnızca, aynı masaüstünden başka bir şeyin çözücüyü çağırabilmesi için onun hangi adresi dinlediğini değiştirir.
3. Adım: çözümü istek yolundan çıkarın
Yukarıdaki sınırlar göz önüne alındığında, AWS Lambda’da bir captcha işi isteğe değil bir kuyruğa aittir. API Gateway’e yanıt veren handler, çözümü yapan handler olmamalıdır: işi kabul edin, kuyruğa koyun ve hemen yanıt verin. İkinci bir function kuyruğu okur ve işi, bir web isteğine değil bir CAPTCHA’ya uygun bir zaman aşımıyla yapar.
import json, os, uuid, boto3
sqs = boto3.client("sqs")
QUEUE_URL = os.environ["QUEUE_URL"]
def lambda_handler(event, context):
"""API Gateway calls this. It never solves anything."""
body = json.loads(event["body"])
job_id = str(uuid.uuid4())
sqs.send_message(
QueueUrl=QUEUE_URL,
MessageBody=json.dumps({
"job_id": job_id,
"sitekey": body["sitekey"],
"pageurl": body["pageurl"],
}),
)
return {"statusCode": 202,
"body": json.dumps({"job_id": job_id})}İş kimliği, çağıranın sonradan soracağı bir şeyi olsun diye oradadır. Tüketiciyi, token’ı geri uzatacak şekilde değil işi kendisi bitirecek şekilde tasarlayın; çünkü ikinci bir HTTP gidiş dönüşünü bekleyen bir token genellikle yolda süresini doldurur.
Asenkron invoke yerine kuyruk kullanın. Lambda, başarısız bir asenkron çağrıyı varsayılan olarak iki kez yeniden dener ve bir CAPTCHA çözümü körlemesine yeniden denenecek şey değildir: ikinci deneme, sayfa bağlamı çoktan değişmiş bir sitekey’den başlar ve çözümün bedelini her durumda ödersiniz. Kuyruk yeniden denemeleri ortadan kaldırmaz, onları görünür ve sınırlı kılar. Kontrol ettiğiniz bir visibility timeout, bir redrive politikası ve sürekli başarısız olan bir işin bakabileceğiniz bir yere düştüğü bir dead letter queue elde edersiniz. AWS, en az beşlik bir maximum receive count öneriyor; bu da mesaj park edilmeden önce throttle edilmiş bir yeniden denemeye yer bırakır.
Kuyruğun visibility timeout değerini tüketici function’ın zaman aşımının en az altı katına kurun; AWS bunu aynı throttling nedeniyle öneriyor. Sıralama isteğe bağlı değildir: Lambda event source mapping’i doğrular ve function zaman aşımı visibility timeout’tan büyükse onu reddeder. Yukarıdaki 330 saniyelik function ile bu, 1980 saniyeye yakın bir visibility timeout demektir.
Tam çalışan örnek
Tüketici. İstemciyi handler’ın dışında bir kez kurar, böylece sıcak bir ortam her mesajda yeniden bağlanmak yerine onu yeniden kullanır.
# pip install capskip
import json, os
from urllib.parse import urlencode
from urllib.request import urlopen
from capskip import (CapSkip, ApiException, NetworkException,
TimeoutException, ValidationException)
# Built at cold start and reused while the environment stays warm.
solver = CapSkip(
host=os.environ["CAPSKIP_HOST"], # Server mode address
port=int(os.environ.get("CAPSKIP_PORT", 8080)),
recaptchaTimeout=300,
)
def lambda_handler(event, context):
failures = []
for record in event["Records"]:
job = json.loads(record["body"])
try:
result = solver.recaptcha(
sitekey=job["sitekey"],
url=job["pageurl"],
)
except NetworkException:
# No route to the solver. Retry this one message.
failures.append({"itemIdentifier": record["messageId"]})
continue
except (ApiException, TimeoutException, ValidationException) as exc:
print("giving up on this job:", exc)
continue
# Use the token here. It is short lived, so do not park it.
urlopen(job["pageurl"], data=urlencode(
{"g-recaptcha-response": result["code"]}).encode())
# Needs ReportBatchItemFailures on the event source mapping.
return {"batchItemFailures": failures}Hata fırlatmak yerine başarısız mesajı raporlayın. Fırlatmak tüm batch’i başarısız kılar ve SQS o zaman, zaten çözdükleriniz de dahil olmak üzere içindeki her mesajı kuyruğa geri koyar; aşağıdaki tablonun uyardığı çift çözüm sorunu da budur. Kısmi batch yanıtı yalnızca başarısız olan kaydı yeniden dener ve dikkate alınabilmesi için event source mapping üzerinde report batch item failures ayarına ihtiyaç duyar.
Bir NetworkException’ı yeniden deneyin, diğer üçünü yutun. Çözücüye giden bir rotanın olmaması, işin sonradan başarılı olabileceği anlamına gelir; çözülemeyen bir CAPTCHA, bir zaman aşımı veya hatalı bir parametre ise her denemede aynı şekilde başarısız olur ve yeniden denemek yalnızca aynı süreyi bir kez daha harcar. Tek bir şey yakalamayı tercih ederseniz, dört SDK istisnasının hepsi CapSkipError’dan türer.
O gönderme işlemi tasarımın tamamının amacıdır: token’ın ne için olduğunu aynı çağrının içinde yapın. Bir reCAPTCHA token’ı yaklaşık iki dakika geçerlidir, dolayısıyla onu sonraki bir adımın alması için bir veritabanına yazmak genellikle süresi çoktan dolmuş bir şeyi almak demektir. O pencerenin ayrıntıları şurada: reCAPTCHA v2 çözücü rehberi. Her SDK çağrısının arkasındaki ham uç noktalar ise şurada belgelenmiştir: API referansı.
Sık görülen hatalar ve anlamları
| Gördüğünüz | Neden | Düzeltme |
|---|---|---|
| 29 saniye sonra API Gateway’den 504 gelirken CloudWatch function’ın hâlâ çalıştığını gösteriyor | Function zaman aşımı değil, entegrasyon zaman aşımı | İsteği hemen yanıtlayın ve çözümü kuyrukta yapın |
| Task timed out after 3.00 seconds | Kimsenin canı yanana kadar değiştirmediği varsayılan function zaman aşımı | İstemcinin yoklama zaman aşımının üstüne çıkarın |
| 127.0.0.1 adresini gösteren bir NetworkException | Sandbox içindeki loopback sandbox’a ulaşır ve CapSkip orada değildir | Server moduna geçin ve host ortam değişkenini ayarlayın |
| Function zaman aşımına uğrayana kadar takılı kalan bir bağlantı | Function, dışarı çıkan rotası olmayan bir VPC’ye bağlı; dolayısıyla paketler reddedilmez, hiçbir yere gitmez | Bir NAT gateway ekleyin. VPC bağlantısını kaldırmak da internet erişimini geri getirir, ama o zaman güvenlik duvarınız tek bir adresi izin listesine alamaz |
| NAT gateway zaten yerindeyken aynı takılma | Function, özel subnet’lere değil genel subnet’e bağlı | Onu, NAT gateway’e yönlendirilmiş olan özel subnet’lere bağlayın |
| Dizüstünüzden çalışıyor, function’dan çalışmıyor | Güvenlik duvarından sizin ev adresinize izin veriliyor, AWS adresine verilmiyor | NAT gateway’in Elastic IP’sine izin verin |
| Bir batch’teki her iş iki kez çözülüyor | Bir kayıt hata fırlattı, bu yüzden SQS zaten başarılı olanlar da dahil batch’in tamamını kuyruğa geri döndürdü | Hata fırlatmak yerine başarısız kaydı raporlayın ve report batch item failures ayarını açın |
| Lambda event source mapping’i oluşturmayı reddediyor | Function zaman aşımı, Lambda’nın doğruladığı kuyruk visibility timeout değerinden büyük | Visibility timeout değerini function zaman aşımının en az altı katına çıkarın |
| İlgisiz bir çağrı sırasında tamamlanan bir çözüm | Handler döndüğünde bir arka plan thread’i donduruldu, sonraki çağrıda ise yeniden devreye girdi | Dönmeden önce çözümü bitirin. Burada gönder ve unut diye bir şey yok |
| 300 saniyeyi gösteren bir TimeoutException | CapSkip, reCAPTCHA yoklama zaman aşımı içinde yanıt vermedi | Çözücünün çalıştığını ve dolu olmadığını kontrol edin. Tavanı yükseltmek aynı cevabı yalnızca geciktirir |
| Elle yazılmış bir yoklama döngüsünde CAPCHA_NOT_READY | Cevap henüz hazır değil; bu normal bir ara durumdur, hata değildir | Yoklamayı SDK yapsın ya da şunu okuyun: o kodun kılavuzu |
| Unable to import module lambda_function, no module named capskip | Paket yanlış mimari veya yanlış yorumlayıcı için kuruldu ya da layer içinde yanlış yolda duruyor | Platform, implementation ve python version bayraklarıyla kurun ve layer içeriğini zip’in kökünde python klasörünün altına koyun |
FAQ
CapSkip’in kendisi Lambda içinde çalışabilir mi?
Hayır ve buna gerek de yok. CapSkip, sizin sahip olduğunuz donanımda çalışan bir Windows uygulamasıdır; function’ınızdaki SDK ise onun HTTP üzerinden çalışan ince bir istemcisidir. Server modunu açın, function’ı o adrese yöneltin; function onu aynı masadaki bir script’in çağıracağı gibi çağırır. Çözüm işi sizin makinenizde kalır ve çözüm sayısının kimse tarafından ölçülüp faturalandırılmamasının nedeni de budur.
API Gateway’in arkasında hiç çözüm yapabilir miyim?
Bazen, ve bu yapılandırmanızdan çok türe bağlı. Bir görüntü CAPTCHA’sı ya da bir ALTCHA iş kanıtı çoğu zaman bir iki saniyede biter ve 29 saniyenin içine rahatça sığar. Bir reCAPTCHA veya bir Turnstile challenge sayfası ise sık sık sığmaz; sığmadığında da iş çalışmaya ve faturalanmaya devam ederken çağıran 504 alır. Ürünün tamamı tek bir senkron uç noktaysa REST API’niz için entegrasyon zaman aşımı artışı talep edin ve kendi trafiğinizin gerçekte ne kadar sürdüğünü ölçün. Yine de sabahın üçünde sizi şaşırtmayacak biçim, kuyruk biçimidir.
Çözücüye yalnızca kendi function’ımın ulaşmasını nasıl sağlarım?
Function’ı bir VPC’ye bağlayın, giden trafiğini bir NAT gateway üzerinden yönlendirin ve o gateway’in Elastic IP’sine güvenlik duvarınızda izin verin. Size tek ve sabit bir kaynak adres veren en basit biçim budur, çünkü VPC dışındaki bir function, altınızdan değişen AWS adreslerinden çıkar. Bir Site-to-Site VPN veya Direct Connect aynı işi internete hiç port açmadan yapar. Çözücünün portunu diğer her şeye kapalı tutun ve API anahtarını tek kilit değil ikinci bir kilit olarak görün.
Uzun süren bir çözüm function’ı pahalı hale getirir mi?
Lambda geçen gerçek süreyi faturalandırır, dolayısıyla cevap bekleyerek oturan bir function, aritmetik yapan bir function ile aynı ücrete tabidir. Bu, kuyruk için ikinci bir gerekçedir: tüketici hesaplama değil ağ beklediği için büyük bir bellek boyutuna ihtiyaç duymaz ve o beklerken yukarı akışta hiçbir şey tıkanmaz. Çözümün kendisi ise CAPTCHA başına size hiçbir şeye mal olmaz, çünkü kendi makinenizde gerçekleşir. Aynı denge barındırılan diğer platformlarda da karşınıza çıkar; oradaki karşılığını Azure Functions rehberi baştan sona işliyor.
Kısa özet
AWS Lambda’da bir captcha çözümü üç karar gerektirir ve üçü de siz handler’ı yazmadan önce verilir. CapSkip’i Server moduna geçirin, çünkü Lambda sandbox’ındaki loopback hiçbir yere ulaşmaz. Function’ı bir VPC’ye bağlayın ve bir NAT gateway üzerinden yönlendirin ki güvenlik duvarınızın izin vereceği tek bir Elastic IP olsun. Sonra istek yolunda çözmeyi bırakın: API Gateway size 29 saniye verir, yavaş bir reCAPTCHA daha fazlasını ister ve erken dönmek işe yaramaz, çünkü handler’ınız döndüğü anda ortam donar. İşi kuyruğa alın, zaman aşımı istemcininkinin üstünde olan bir tüketicide çözün ve token’ı onu kazanan çağrının içinde kullanın.
Python paketinin sunduğu her metot ve her birinin aldığı seçenekler şurada listeleniyor: Python CAPTCHA çözücü sayfası.
Ekonomi tarafında son bir nokta, çünkü kuyruk tasarımını rahat kılan şey budur. Çöpe giden bir iş size Lambda milisaniyelerinden başka hiçbir şeye mal olmaz: captcha atlatma işi zaten parasını ödediğiniz bir donanımda gerçekleşir, dolayısıyla bir işi yeniden denemek ya da süresi dolmuş bir token’ı atmak hiç kimseden gelen hiçbir faturada görünmez.
