AWS Lambda में कैप्चा कैसे हल करें, बिना टाइमआउट हुए

AWS Lambda में कैप्चा हल करना दो जगह नाकाम होता है, और दोनों में से कोई आपका कोड नहीं है। API Gateway 29 सेकंड बाद function का इंतज़ार छोड़ देता है, इसलिए 40 सेकंड लेने वाला reCAPTCHA caller को 504 लौटा देता है जबकि function अब भी काम कर रहा होता है। और Lambda sandbox के अंदर 127.0.0.1 खुद वही sandbox है, इसलिए loopback पर टिका क्लाइंट वहाँ किसी को सुनते हुए नहीं पाता। CapSkip उस मशीन पर चलता है जो आपकी अपनी है, और इस सेटअप में वह कभी वह मशीन नहीं होती जिस पर आपका function चलता है। पहले पता ठीक करें, फिर solve को request के रास्ते से हटाएँ।
आपको क्या चाहिए
- आपके नियंत्रण वाली किसी Windows मशीन पर चलता हुआ CapSkip। यह एक डेस्कटॉप ऐप्लिकेशन है और Lambda के अंदर नहीं चलता। यहाँ function सिर्फ़ क्लाइंट है, इससे ज़्यादा कुछ नहीं।
- Python 3.10 या उससे नया Lambda runtime, जिसमें CapSkip पैकेज deployment package में हो या किसी layer में।
- Server मोड चालू। Local मोड 127.0.0.1 पर सिर्फ़ उसी डिवाइस को जवाब देता है, जो AWS में चल रहे किसी function के काम का नहीं। Server मोड आपके नेटवर्क पते या पब्लिक IP पर सुनता है ताकि function उसी API से उस तक पहुँच सके, और दोनों यहाँ मिलते हैं: कनेक्शन सेटिंग्स. स्टैटिक पब्लिक IP की सलाह दी जाती है, साथ में उस एक पते के लिए firewall नियम जहाँ से AWS आएगा।
- function से उस पते तक पहुँचने का कोई रास्ता। दोनों शक्लें Step 2 में हैं, क्योंकि VPC से जुड़ा function उससे अलग बर्ताव करता है जो जुड़ा नहीं है।
API Gateway का 29 सेकंड वाला timeout डिज़ाइन क्यों तय करता है
AWS Lambda में कैप्चा हल करने को तीन सीमाओं के भीतर बैठना होता है। कोई भी कोड लिखने से पहले इन्हें लिख लें, क्योंकि मिलकर ये सबसे स्पष्ट दिखने वाले डिज़ाइन को खारिज कर देती हैं।
| Limit | मान | क्या इसे बढ़ा सकते हैं |
|---|---|---|
| API Gateway का integration timeout | डिफ़ॉल्ट रूप से 29 सेकंड | Regional और private REST API पर, quota request से। AWS चेतावनी देता है कि इस बढ़ोतरी की कीमत आपके account throttle quota से चुकानी पड़ सकती है |
| Lambda function का timeout | डिफ़ॉल्ट रूप से 3 सेकंड, सामान्य function के लिए ज़्यादा से ज़्यादा 900 सेकंड | हाँ, उसी 15 मिनट की छत तक |
| CapSkip का polling timeout | reCAPTCHA, Turnstile और GeeTest के लिए 300 सेकंड, इमेज और ALTCHA के लिए 120 | हाँ, दोनों constructor के options हैं |
यानी कोई synchronous API कॉल धीमे reCAPTCHA को नहीं सँभाल सकती। function के पास उसके लिए जगह है, आगे खड़े gateway के पास नहीं, और caller को 504 दिख जाता है जबकि solve अब भी चल रहा होता है और उसका बिल भी बन रहा होता है।
इसके बाद लोग जिस जुगाड़ की ओर बढ़ते हैं वह और बुरा है। जल्दी return कर देना और solve को किसी background thread पर पूरा करना काम नहीं करता, क्योंकि handler के लौटने के बाद Lambda execution environment को जमा देता है। AWS इसे साफ़ शब्दों में कहता है: जो background प्रोसेस या callback function के खत्म होते समय पूरे नहीं हुए थे, वे तब फिर से शुरू होते हैं जब Lambda उसी environment को दोबारा इस्तेमाल करता है। फिर से शुरू, आगे नहीं बढ़ते। आपका thread मिनटों बाद जागता है, ऐसे कैप्चा के लिए polling के बीच में जिसका token बहुत पहले expire हो चुका है, और वह भी ऐसी invocation के भीतर जिससे उसका कोई लेना-देना नहीं। कहीं कोई error नहीं आता। काम बस गलत जगह जा गिरता है। AWS ने इस lifecycle को यहाँ विस्तार से लिखा है: अपनी execution environment गाइड.
Step 1: SDK को पैकेज करें और function configure करें
किसी फ़ोल्डर में इंस्टॉल करके उसे अपने handler के साथ zip कर दें, या python नाम के फ़ोल्डर में इंस्टॉल करें, उसे zip करें और layer के रूप में जोड़ दें। platform और interpreter को उसी पर टिकाएँ जो function चलाता है, अपने लैपटॉप वाले पर नहीं, वरना cold start पर import फेल होगा और लॉग में कुछ काम का नहीं मिलेगा। इन flags के बिना pip आपके लोकल Python के हिसाब से wheels चुनता है, और नए interpreter के लिए बना wheel उस runtime पर लोड नहीं होगा।
# 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
फिर timeout और connection की जानकारी कोड में नहीं, बल्कि configuration के तौर पर सेट करें, ताकि वही एक पैकेज टेस्ट सॉल्वर और प्रोडक्शन सॉल्वर, दोनों के साथ चले।
# 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 का timeout क्लाइंट के अपने polling timeout से थोड़ा ऊपर रखें, नीचे नहीं। नीचे रखा तो Lambda पहले invocation को मार देगा, और CloudWatch में उस TimeoutException के बजाय सिर्फ़ एक सूखा task timeout मिलेगा जो आपको बताता कि हुआ क्या था।
Step 2: function को एक NAT gateway और एक Elastic IP दें
यही वह क़दम है जो तय करता है कि कुछ चलेगा भी या नहीं, और जवाब एक ऐसी सेटिंग पर टिका है जिसे शायद आपने नेटवर्किंग की चीज़ माना ही न हो।
| function का configuration | वह किस तक पहुँच सकता है | आप firewall पर किसे अनुमति देते हैं |
|---|---|---|
| किसी VPC से जुड़ा नहीं | सीधे पब्लिक इंटरनेट | कुछ भी काम का नहीं। ट्रैफ़िक AWS के अपने पतों से निकलता है जो बदलते रहते हैं, इसलिए किसी एक IP को allowlist नहीं किया जा सकता |
| VPC से जुड़ा, कोई NAT gateway नहीं | सिर्फ़ वही जो उस VPC के अंदर है। आपका सॉल्वर उसमें नहीं है | कुछ नहीं। कनेक्शन मना होने के बजाय timeout हो जाता है |
| VPC से जुड़ा, NAT gateway से होकर रूट किया गया | पब्लिक इंटरनेट, एक ही पते से | NAT gateway का Elastic IP, यही वह शक्ल है जो आपको चाहिए |
पहली दो पंक्तियाँ Lambda सीधे दस्तावेज़ में बताता है: functions को डिफ़ॉल्ट रूप से पब्लिक इंटरनेट मिलता है, और किसी को VPC से जोड़ देने पर वह उसी VPC के अंदर के संसाधनों तक सिमट जाता है, जब तक कि function के subnets के पास बाहर जाने का रास्ता न हो। वह रास्ता एक public subnet में बैठा NAT gateway है, जिसका ब्योरा यहाँ है: Lambda इंटरनेट एक्सेस गाइड. काम की बात इसका साइड इफ़ेक्ट है। NAT gateway एक Elastic IP रखता है, इसलिए हर solve आपकी मशीन तक एक ही स्थिर पते से पहुँचता है और आपका firewall नियम एक ही लाइन का हो सकता है।
function को private subnets से जोड़ें, public वाले से नहीं। NAT gateway पहले से लगा होने के बावजूद अटकाव यहीं से आता है: public subnet से जुड़े function के पास इंटरनेट एक्सेस होता ही नहीं, route table चाहे जो कहे, इसलिए packets बस कहीं नहीं जाते। वही गाइड यह बात दो बार दोहराती है।
एक स्थिर source पता देने वाली सबसे सरल शक्ल NAT gateway है, इकलौती नहीं। उसी VPC से Site-to-Site VPN या Direct Connect आपके अपने नेटवर्क पर बैठे सॉल्वर तक पहुँच जाता है और उसका पोर्ट इंटरनेट के सामने बिल्कुल नहीं खोलता, और अगर मशीन ऐसी जगह है जहाँ आप पोर्ट खोलना नहीं चाहेंगे तो दोनों की सेटअप मेहनत वसूल है। जो भी रास्ता चुनें, सॉल्वर का पोर्ट बाकी सबके लिए बंद रखें। Server मोड में हार्डवेयर अब भी आपका है और उस पर अब भी कोई मीटर नहीं चलता: वह सिर्फ़ यह बदलता है कि सॉल्वर कहाँ सुनता है, ताकि उसी डेस्कटॉप के अलावा भी कोई उसे बुला सके।
Step 3: solve को request के रास्ते से हटाएँ
ऊपर की सीमाओं को देखते हुए, AWS Lambda में कैप्चा का काम request पर नहीं, किसी queue पर होना चाहिए। जो handler API Gateway को जवाब देता है वही handler हल न करे: job स्वीकार करें, उसे queue पर डालें, और तुरंत जवाब दे दें। दूसरा function queue पढ़ता है और ऐसे timeout के साथ काम करता है जो वेब request के बजाय कैप्चा के हिसाब का हो।
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})}job id इसलिए है ताकि caller के पास बाद में पूछने को कुछ हो। consumer को इस तरह बनाएँ कि वह token वापस थमाने के बजाय काम खुद पूरा करे, क्योंकि जो token दूसरे HTTP राउंड ट्रिप का इंतज़ार करता है वह अक्सर रास्ते में ही expire हो जाता है।
asynchronous invoke के बजाय queue इस्तेमाल करें। नाकाम asynchronous invocation को Lambda डिफ़ॉल्ट रूप से दो बार दोहराता है, और कैप्चा solve वह चीज़ नहीं जिसे आँख मूँदकर दोहराया जाए: दूसरी कोशिश ऐसी sitekey से शुरू होती है जिसका पेज संदर्भ आगे बढ़ चुका है, और solve की कीमत आपको दोनों हाल में चुकानी है। queue retries हटाती नहीं, उन्हें दिखने लायक़ और सीमित बना देती है। आपको अपने नियंत्रण वाला visibility timeout मिलता है, एक redrive policy मिलती है, और एक dead letter queue मिलती है जहाँ बार-बार नाकाम होने वाली job ऐसी जगह जा गिरती है जिसे आप देख सकते हैं। AWS कम से कम पाँच के maximum receive count की सिफ़ारिश करता है, जिससे संदेश को किनारे लगाने से पहले किसी throttled retry की गुंजाइश बची रहती है।
queue का visibility timeout consumer function के timeout से कम से कम छह गुना रखें, जिसकी सिफ़ारिश AWS उसी throttling वजह से करता है। यह क्रम वैकल्पिक नहीं है: Lambda event source mapping को जाँचता है और अगर function का timeout visibility timeout से बड़ा हो तो उसे मना कर देता है। ऊपर वाले 330 सेकंड के function के साथ इसका मतलब है 1980 सेकंड के आसपास का visibility timeout।
पूरा चलने वाला उदाहरण
यह रहा consumer। यह क्लाइंट को handler के बाहर एक ही बार बनाता है, ताकि गर्म environment हर संदेश पर दोबारा जुड़ने के बजाय उसी को दोबारा इस्तेमाल करे।
# 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}exception उठाने के बजाय नाकाम संदेश की रिपोर्ट करें। exception उठाने से पूरा batch फेल होता है, और तब SQS उसका हर संदेश queue में लौटा देता है, उन्हें भी जिन्हें आप हल कर चुके थे, और यही वह दोहरे solve वाली दिक्कत है जिसकी चेतावनी नीचे की तालिका देती है। partial batch response सिर्फ़ उसी record को दोबारा चलाती है जो फेल हुआ था, और इसके लिए event source mapping पर report batch item failures सेटिंग चालू होनी चाहिए।
NetworkException को दोबारा आज़माएँ और बाकी तीन को चुपचाप निगल जाएँ। सॉल्वर तक रास्ता न होने का मतलब है कि job बाद में कामयाब हो सकती है, जबकि न हल होने वाला कैप्चा, timeout या गलत parameter हर कोशिश में उसी तरह फेल होगा और उसे दोहराने में बस वही समय दोबारा खर्च होगा। अगर आप एक ही चीज़ catch करना चाहें तो चारों SDK exceptions CapSkipError से निकलते हैं।
वह submit ही पूरे डिज़ाइन का मक़सद है: token जिस काम के लिए है, वह काम उसी invocation के भीतर कर लें। reCAPTCHA token करीब दो मिनट चलता है, इसलिए उसे किसी डेटाबेस में लिखकर बाद वाले क़दम के लिए छोड़ देने का मतलब आम तौर पर यही होता है कि आप वह उठाएँगे जो expire हो चुका है। उस खिड़की का पूरा ब्योरा यहाँ है: reCAPTCHA v2 सॉल्वर गाइड, और हर SDK कॉल के पीछे मौजूद कच्चे endpoint यहाँ दस्तावेज़ित हैं: API रेफ़रेंस.
आम errors और उनका मतलब
| आप जो देखते हैं | कारण | फिक्स |
|---|---|---|
| 29 सेकंड बाद API Gateway से 504, जबकि CloudWatch में function अब भी चलता दिखता है | integration timeout, function का timeout नहीं | request का जवाब तुरंत दें और solve queue पर करें |
| Task timed out after 3.00 seconds | function का डिफ़ॉल्ट timeout, जिसे कोई तब तक नहीं बदलता जब तक वह काट न ले | इसे क्लाइंट के polling timeout से ऊपर कर दें |
| 127.0.0.1 का नाम लेता हुआ NetworkException | sandbox के अंदर loopback उसी sandbox तक पहुँचता है, और CapSkip उसमें है ही नहीं | Server मोड पर जाएँ और host environment variable सेट करें |
| ऐसा कनेक्शन जो function के timeout होने तक अटका रहता है | function ऐसे VPC से जुड़ा है जिसमें बाहर जाने का रास्ता नहीं, इसलिए packets मना होने के बजाय कहीं नहीं जाते | एक NAT gateway जोड़ें। VPC से हटा देने पर भी इंटरनेट एक्सेस लौट आता है, लेकिन तब आपका firewall किसी एक पते को allowlist नहीं कर पाएगा |
| NAT gateway पहले से लगा होने पर भी वही अटकाव | function private subnets के बजाय public subnet से जुड़ा है | उसे private subnets से जोड़ें, क्योंकि रूटिंग NAT gateway तक उन्हीं की होती है |
| आपके लैपटॉप से चलता है, function से नहीं | आपके घर के पते को firewall से गुज़रने की अनुमति है, AWS वाले को नहीं | NAT gateway के Elastic IP को अनुमति दें |
| एक batch की हर job दो बार हल हुई | एक record ने exception उठाया, इसलिए SQS ने पूरा batch लौटा दिया, उन records समेत जो पहले ही कामयाब हो चुके थे | exception उठाने के बजाय नाकाम record की रिपोर्ट करें, और report batch item failures चालू करें |
| Lambda event source mapping बनाने से मना कर देता है | function का timeout queue के visibility timeout से बड़ा है, और Lambda इसे जाँचता है | visibility timeout को function के timeout से कम से कम छह गुना कर दें |
| ऐसा solve जो किसी असंबंधित invocation के दौरान पूरा होता है | handler के लौटते ही एक background thread जम गया था और अगली कॉल पर पिघल गया | लौटने से पहले solve पूरा करें। यहाँ चलाकर भूल जाने जैसा कुछ नहीं है |
| 300 सेकंड का नाम लेता हुआ TimeoutException | CapSkip ने reCAPTCHA के polling timeout के भीतर जवाब नहीं दिया | देख लें कि सॉल्वर चल रहा है और भरा हुआ नहीं है। सीमा बढ़ाने से वही जवाब बस देर से मिलेगा |
| हाथ से लिखे polling loop में CAPCHA_NOT_READY | जवाब अभी तैयार नहीं है, जो एक सामान्य बीच की स्थिति है, कोई error नहीं | SDK को poll करने दें, या यह पढ़ें: उस code की गाइड |
| Unable to import module lambda_function, no module named capskip | पैकेज गलत architecture या interpreter के लिए इंस्टॉल हुआ था, या layer में गलत path पर पड़ा है | platform, implementation और python version वाले flags के साथ इंस्टॉल करें, और layer की सामग्री zip की जड़ में python के नीचे रखें |
FAQ
क्या CapSkip खुद Lambda के अंदर चल सकता है?
नहीं, और इसकी ज़रूरत भी नहीं। CapSkip एक Windows ऐप्लिकेशन है जो आपके अपने हार्डवेयर पर चलता है, और आपके function में मौजूद SDK उसका HTTP पर चलने वाला पतला क्लाइंट भर है। Server मोड चालू करें, function को उसी पते पर सेट करें, और function उसे ठीक वैसे ही बुलाएगा जैसे उसी मेज़ पर रखी कोई script बुलाती। हल करने का काम आपकी मशीन पर ही रहता है, और यही वजह है कि कितने solve हुए, इसका हिसाब कोई नहीं रखता।
क्या API Gateway के पीछे हल करना मुमकिन है?
कभी-कभी, और यह आपके configuration पर नहीं, कैप्चा के टाइप पर निर्भर करता है। इमेज कैप्चा या ALTCHA का proof of work अक्सर एक-दो सेकंड में निपट जाता है, जो 29 सेकंड के भीतर जगह छोड़कर बैठ जाता है। reCAPTCHA या Turnstile का challenge पेज अक्सर नहीं निपटता, और जब नहीं निपटता तो caller को 504 मिल जाता है जबकि काम चलता रहता है और उसका बिल भी बनता रहता है। अगर पूरा प्रोडक्ट एक ही synchronous endpoint है, तो अपने REST API के लिए integration timeout बढ़ाने की माँग करें और नापें कि आपके अपने ट्रैफ़िक को असल में कितना समय लगता है। फिर भी queue वाली शक्ल ही वह है जो रात के तीन बजे आपको नहीं चौंकाती।
मैं सिर्फ़ अपने function को सॉल्वर तक कैसे पहुँचने दूँ?
function को किसी VPC से जोड़ें, उसका बाहर जाने वाला ट्रैफ़िक NAT gateway से होकर रूट करें, और उस gateway के Elastic IP को अपने firewall पर अनुमति दें। एक स्थिर source पता देने वाली यही सबसे सरल शक्ल है, क्योंकि VPC के बाहर का function AWS के अपने पतों से निकलता है जो आपके नीचे से बदलते रहते हैं। Site-to-Site VPN या Direct Connect यही काम इंटरनेट के लिए कोई पोर्ट खोले बिना कर देता है। सॉल्वर का पोर्ट बाकी सबके लिए बंद रखें, और API key को इकलौता नहीं, दूसरा ताला मानें।
क्या लंबा solve function को महँगा बना देता है?
Lambda घड़ी के हिसाब से बिल बनाता है, इसलिए जो function जवाब का इंतज़ार करता बैठा है उसका दाम उतना ही है जितना गणित करते किसी function का। यह queue के पक्ष में दूसरी दलील है: consumer को बड़ी memory size की ज़रूरत नहीं, क्योंकि वह गणना नहीं, नेटवर्क का इंतज़ार कर रहा होता है, और उसके इंतज़ार के दौरान ऊपर की तरफ़ कुछ भी नहीं रुकता। solve की खुद आपको प्रति कैप्चा कोई कीमत नहीं पड़ती, क्योंकि वह आपकी अपनी मशीन पर होता है। यही समझौता दूसरे hosted प्लेटफ़ॉर्म पर भी सामने आता है, और Azure Functions गाइड वहाँ का वही रास्ता क़दम दर क़दम बताती है।
संक्षेप में
AWS Lambda में कैप्चा हल करने के लिए तीन फ़ैसले चाहिए और तीनों handler लिखने से पहले हो जाते हैं। CapSkip को Server मोड पर करें, क्योंकि Lambda sandbox में loopback कहीं नहीं पहुँचता। function को किसी VPC से जोड़ें और उसे NAT gateway से होकर रूट करें, ताकि आपके firewall के पास अनुमति देने को एक Elastic IP हो। फिर request के रास्ते पर हल करना बंद करें: API Gateway आपको 29 सेकंड देता है, धीमे reCAPTCHA को इससे ज़्यादा चाहिए, और जल्दी लौट आने से कोई मदद नहीं मिलती क्योंकि जिस पल आपका handler रुकता है उसी पल environment भी जम जाता है। job को queue पर डालें, उसे ऐसे consumer में हल करें जिसका timeout क्लाइंट वाले से ऊपर हो, और token को उसी invocation में इस्तेमाल करें जिसने उसे कमाया है।
Python पैकेज जो भी method देता है, और हर एक कौन से options लेती है, सब यहाँ सूचीबद्ध है: Python कैप्चा सॉल्वर पेज.
आख़िरी बात अर्थशास्त्र की, क्योंकि यही queue वाले डिज़ाइन को सहज बनाती है। फेंकी गई job आपको सिर्फ़ Lambda के milliseconds की पड़ती है, इसके अलावा कुछ नहीं: कैप्चा बायपास का काम उसी हार्डवेयर पर होता है जिसके पैसे आप पहले ही दे चुके हैं, इसलिए किसी job को दोबारा चलाना या expire हो चुका token फेंक देना कभी किसी के बिल में नहीं दिखता।
