معماری سرورلس به استانداردی برای برنامههای وب مدرن تبدیل شده است، اما توسعهدهندگان به طور مرتب با مشکلی مواجه هستند: همه درخواستها از توابع Lambda یا Edge Functions با آدرسهای IP مراکز داده ارائهدهندگان ابری ارسال میشوند. این امر منجر به مسدودیتها هنگام دسترسی به APIهای خارجی، استخراج دادهها یا خودکارسازی وظایف میشود. در این راهنما بررسی میکنیم که چگونه پروکسی را در توابع سرورلس ادغام کنیم تا محدودیتها، محدودیتهای نرخ و مسدودیتهای جغرافیایی را دور بزنیم.
چرا توابع سرورلس به پروکسی نیاز دارند
پلتفرمهای سرورلس (AWS Lambda، Google Cloud Functions، Vercel، Cloudflare Workers) کد را در زیرساخت ابری اجرا میکنند و از آدرسهای IP مراکز داده استفاده میکنند. این امر چندین مشکل حیاتی برای توسعهدهندگان ایجاد میکند:
مشکل 1: مسدودیتهای IP مراکز داده. بسیاری از سرویسها به طور خودکار درخواستها را از آدرسهای IP شناخته شده AWS، Google Cloud یا Azure مسدود میکنند. به عنوان مثال، هنگام استخراج دادهها از وبسایتهای تجارت الکترونیک (Amazon، eBay، Wildberries) یا شبکههای اجتماعی (Instagram API، TikTok API) توابع Lambda شما در اولین درخواست HTTP 403 یا captcha دریافت خواهند کرد. سیستمهای محافظت در برابر رباتها (Cloudflare، Akamai، DataDome) به سرعت ترافیک را از مراکز داده ابری شناسایی میکنند.
مشکل 2: محدودیت نرخ در سطح IP. اگر شما یک برنامه سرورلس با هزاران فراخوانی همزمان راهاندازی کنید، همه درخواستها ممکن است از یک یا چند آدرس IP AWS ارسال شوند. APIهای خارجی به سرعت به محدودیتها میرسند (به عنوان مثال، GitHub API — 60 درخواست در ساعت از یک IP، Google Maps API — 100 درخواست در ثانیه). حتی اگر شما یک طرح API پیشرفته را پرداخت کرده باشید، محدودیت IP همچنان اعمال خواهد شد.
مشکل 3: مسدودیتهای جغرافیایی. توابع سرورلس در منطقه us-east-1 نمیتوانند به محتوایی که فقط از روسیه، اروپا یا آسیا قابل دسترسی است، دسترسی پیدا کنند. این موضوع در هنگام استخراج دادهها از بازارهای منطقهای (Ozon، Yandex.Market)، بررسی تبلیغات از کشورهای مختلف یا آزمایش محلیسازی وبسایتها بحرانی است.
مشکل 4: IP مشترک با سایر کاربران. در محیط سرورلس، توابع شما ممکن است آدرس IPای را دریافت کنند که قبلاً توسط سایر مشتریان ارائهدهنده ابری استفاده شده است. اگر کسی قبلاً از این IP سوءاستفاده کرده باشد (اسپم، DDoS، استخراج دادهها)، ممکن است در لیستهای سیاه قرار گیرد. شما بدون هیچ دلیلی مسدود خواهید شد.
راهحل تمام این مشکلات — ادغام سرورهای پروکسی است. پروکسی به توابع سرورلس شما اجازه میدهد تا درخواستها را از طریق آدرسهای IP مسکونی یا موبایل ارسال کنند که به عنوان کاربران عادی به نظر میرسند. این امر مسدودیتها را برطرف میکند، محدودیتهای نرخ را دور میزند و دسترسی به محتوای مسدود شده جغرافیایی را فراهم میکند.
کدام نوع پروکسی برای سرورلس مناسب است
انتخاب نوع پروکسی بستگی به وظیفه برنامه سرورلس شما دارد. سه گزینه اصلی و سناریوهای استفاده از آنها را بررسی میکنیم:
| نوع پروکسی | سرعت | ناشناس بودن | سناریوهای استفاده |
|---|---|---|---|
| پروکسی مراکز داده | بسیار بالا (10-50 میلیثانیه) | پایین | دسترسی به API بدون محدودیتهای سخت، بررسی در دسترس بودن خدمات، نظارت بر uptime |
| پروکسیهای مسکونی | متوسط (100-500 میلیثانیه) | بالا | استخراج دادههای تجارت الکترونیک، کار با شبکههای اجتماعی، دور زدن Cloudflare، دسترسی به محتوای مسدود شده جغرافیایی |
| پروکسیهای موبایل | متوسط (150-600 میلیثانیه) | بسیار بالا | کار با APIهای موبایل (Instagram، TikTok)، آزمایش برنامههای موبایل، دور زدن سختترین محافظتها |
برای اکثر برنامههای سرورلس، استفاده از پروکسیهای مسکونی توصیه میشود. آنها تعادل بهینهای بین سرعت و ناشناس بودن فراهم میکنند. IPهای مسکونی به عنوان کاربران عادی به نظر میرسند، که این امکان را فراهم میکند تا از محافظت در برابر رباتها و محدودیتهای نرخ بدون افزایش قابل توجهی در تأخیر عبور کرد.
پروکسیهای مراکز داده فقط برای وظایف ساده مناسب هستند (بررسی وضعیت HTTP، کار با APIهای عمومی بدون محدودیت). پروکسیهای موبایل در موارد خاص نیاز هستند — زمانی که شما با APIهای موبایل کار میکنید یا ناشناس بودن حداکثری برای شما حیاتی است.
تنظیم پروکسی در AWS Lambda
AWS Lambda — محبوبترین پلتفرم سرورلس است و ادغام پروکسی در اینجا نیاز به تنظیم صحیح HTTP-client دارد. توابع Lambda میتوانند از زبانهای برنامهنویسی مختلفی (Node.js، Python، Go) استفاده کنند، بیایید مثالهایی برای رایجترین آنها بررسی کنیم.
Node.js (axios)
Axios — محبوبترین کتابخانه برای درخواستهای HTTP در Node.js است. برای تنظیم پروکسی از پارامتر proxy در پیکربندی استفاده کنید:
const axios = require('axios');
exports.handler = async (event) => {
const proxyConfig = {
host: 'proxy.example.com',
port: 8080,
auth: {
username: 'your_username',
password: 'your_password'
},
protocol: 'http'
};
try {
const response = await axios.get('https://api.example.com/data', {
proxy: proxyConfig,
timeout: 10000 // 10 ثانیه
});
return {
statusCode: 200,
body: JSON.stringify(response.data)
};
} catch (error) {
console.error('خطای پروکسی:', error.message);
return {
statusCode: 500,
body: JSON.stringify({ error: error.message })
};
}
};
نکته مهم: اطلاعات ورود پروکسی را در AWS Systems Manager Parameter Store یا AWS Secrets Manager ذخیره کنید، نه در کد. این کار امنیت را تضمین میکند و به شما این امکان را میدهد که پروکسی را به راحتی بدون بازسازی تابع تغییر دهید.
Python (requests)
در Python برای کار با پروکسی از کتابخانه requests با پارامتر proxies استفاده میشود:
import requests
import json
def lambda_handler(event, context):
proxies = {
'http': 'http://username:password@proxy.example.com:8080',
'https': 'http://username:password@proxy.example.com:8080'
}
try:
response = requests.get(
'https://api.example.com/data',
proxies=proxies,
timeout=10
)
return {
'statusCode': 200,
'body': json.dumps(response.json())
}
except requests.exceptions.RequestException as e:
print(f'خطای پروکسی: {str(e)}')
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
برای پروکسیهای SOCKS5 (پروتکل امنتر) در Python باید کتابخانه اضافی requests[socks] را نصب کرده و فرمت URL را تغییر دهید:
proxies = {
'http': 'socks5://username:password@proxy.example.com:1080',
'https': 'socks5://username:password@proxy.example.com:1080'
}
بهینهسازی برای شروعهای سرد
توابع Lambda با مشکل شروعهای سرد مواجه هستند — اولین درخواست پس از یک دوره بیکاری 1-3 ثانیه طول میکشد. هنگام استفاده از پروکسی، این زمان افزایش مییابد. برای کاهش تأخیرها، HTTP-client را خارج از تابع handler ایجاد کنید:
const axios = require('axios');
// یک بار در زمان راهاندازی کانتینر، کلاینت را ایجاد کنید
const httpClient = axios.create({
proxy: {
host: 'proxy.example.com',
port: 8080,
auth: {
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS
}
},
timeout: 10000
});
exports.handler = async (event) => {
// در هر فراخوانی از کلاینت مجدداً استفاده کنید
const response = await httpClient.get('https://api.example.com/data');
return {
statusCode: 200,
body: JSON.stringify(response.data)
};
};
این رویکرد زمان شروع سرد را 200-500 میلیثانیه کاهش میدهد، زیرا پیکربندی پروکسی تنها یک بار در زمان ایجاد کانتینر Lambda انجام میشود.
ادغام پروکسی در Vercel Edge Functions
Vercel دو نوع تابع سرورلس ارائه میدهد: توابع Node.js (معادل Lambda) و Edge Functions (که در CDN اجرا میشوند). Edge Functions در runtimeای نزدیک به Cloudflare Workers کار میکنند و محدودیتهایی در استفاده از APIهای Node.js دارند. هر دو گزینه را بررسی میکنیم.
توابع Vercel Node.js
برای توابع معمولی Vercel از همان رویکردی که برای AWS Lambda استفاده میکنید، استفاده کنید. یک فایل api/fetch-data.js ایجاد کنید:
import axios from 'axios';
export default async function handler(req, res) {
const proxyConfig = {
host: process.env.PROXY_HOST,
port: parseInt(process.env.PROXY_PORT),
auth: {
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS
}
};
try {
const response = await axios.get(req.query.url, {
proxy: proxyConfig,
timeout: 8000
});
res.status(200).json(response.data);
} catch (error) {
res.status(500).json({ error: error.message });
}
}
متغیرهای محیطی را در Vercel Dashboard (تنظیمات → متغیرهای محیطی) اضافه کنید: PROXY_HOST، PROXY_PORT، PROXY_USER، PROXY_PASS.
توابع Vercel Edge
Edge Functions از Web Fetch API به جای کتابخانههای Node.js استفاده میکنند. پروکسی از طریق هدرهای سفارشی یا middleware تنظیم میشود:
export const config = {
runtime: 'edge',
};
export default async function handler(req) {
const proxyUrl = `http://${process.env.PROXY_USER}:${process.env.PROXY_PASS}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
// برای Edge Runtime نیاز به fetch با پروکسی از طریق agent (نیاز به polyfill دارد)
// گزینه دیگر: استفاده از API پروکسی به طور مستقیم
const targetUrl = new URL(req.url).searchParams.get('target');
const response = await fetch(targetUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
return new Response(await response.text(), {
status: response.status,
headers: response.headers
});
}
محدودیت مهم: Edge Runtime از پروکسیهای استاندارد Node.js پشتیبانی نمیکند. برای کارکرد کامل با پروکسی، استفاده از توابع Node.js یا ایجاد یک سرور پروکسی میانه در یک سرور جداگانه که درخواستها را از Edge Functions دریافت کند، توصیه میشود.
پروکسی در Cloudflare Workers
Cloudflare Workers در V8 isolates کار میکنند و محدودیتهای سختتری نسبت به Vercel Edge Functions دارند. روش استاندارد اتصال پروکسی از طریق کتابخانههای Node.js در اینجا کار نمیکند. دو رویکرد عملی وجود دارد:
روش 1: تونلسازی HTTP CONNECT
از پروکسی که از روش HTTP CONNECT پشتیبانی میکند استفاده کنید. یک تونل از طریق سرور پروکسی ایجاد کنید:
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const proxyUrl = 'http://proxy.example.com:8080';
const targetUrl = 'https://api.example.com/data';
const proxyAuth = btoa(`${PROXY_USER}:${PROXY_PASS}`);
const response = await fetch(proxyUrl, {
method: 'CONNECT',
headers: {
'Host': new URL(targetUrl).host,
'Proxy-Authorization': `Basic ${proxyAuth}`
}
});
if (response.status === 200) {
// تونل برقرار شده، درخواست اصلی را انجام دهید
const finalResponse = await fetch(targetUrl, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
}
});
return finalResponse;
}
return new Response('اتصال پروکسی ناموفق بود', { status: 502 });
}
این روش فقط با پروکسیهای HTTP که از CONNECT پشتیبانی میکنند کار میکند. بیشتر ارائهدهندگان پروکسی مسکونی چنین امکانی را فراهم میکنند.
روش 2: دروازه پروکسی (توصیه میشود)
روش مطمئنتر — راهاندازی یک دروازه پروکسی میانه در یک سرور جداگانه (به عنوان مثال، روی VPS یا AWS EC2). Cloudflare Worker درخواستها را به دروازه شما ارسال میکند و آن دروازه سپس آنها را از طریق پروکسی ارسال میکند:
// Cloudflare Worker
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const targetUrl = new URL(request.url).searchParams.get('url');
const gatewayUrl = 'https://your-proxy-gateway.com/fetch';
const response = await fetch(gatewayUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY // محافظت از دروازه شما
},
body: JSON.stringify({
url: targetUrl,
method: 'GET'
})
});
return response;
}
در سمت دروازه پروکسی (سرور Node.js):
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
const proxyConfig = {
host: 'proxy.example.com',
port: 8080,
auth: {
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS
}
};
app.post('/fetch', async (req, res) => {
if (req.headers['x-api-key'] !== process.env.API_KEY) {
return res.status(401).json({ error: 'غیرمجاز' });
}
try {
const response = await axios({
url: req.body.url,
method: req.body.method || 'GET',
proxy: proxyConfig,
timeout: 10000
});
res.json(response.data);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000);
این رویکرد یک hop اضافی اضافه میکند (افزایش تأخیر به 50-100 میلیثانیه)، اما سازگاری و کنترل کامل بر روی اتصال پروکسی را فراهم میکند.
چرخش آدرسهای IP در محیط سرورلس
یکی از دلایل اصلی استفاده از پروکسی — توزیع درخواستها بین چندین آدرس IP برای دور زدن محدودیتهای نرخ است. در معماری سرورلس دو رویکرد برای چرخش وجود دارد:
چرخش خودکار در سمت ارائهدهنده پروکسی
بیشتر ارائهدهندگان پروکسی مسکونی پروکسیهای چرخشی را ارائه میدهند — شما به یک endpoint متصل میشوید و IP به طور خودکار در هر درخواست یا از طریق یک بازه زمانی مشخص (به عنوان مثال، هر 5 دقیقه) تغییر میکند. این سادهترین گزینه برای سرورلس است:
// یک endpoint، IP به طور خودکار تغییر میکند
const proxyConfig = {
host: 'rotating.proxy.example.com',
port: 8080,
auth: {
username: 'user-session-' + Date.now(), // جلسه منحصر به فرد
password: 'password'
}
};
برخی از ارائهدهندگان اجازه میدهند که چرخش را از طریق پارامترها در username مدیریت کنید: user-session-random (IP جدید در هر درخواست)، user-session-sticky-300 (یک IP به مدت 300 ثانیه).
چرخش دستی از طریق مجموعه پروکسی
اگر شما یک لیست از پروکسیهای استاتیک دارید (به عنوان مثال، پروکسیهای اختصاصی خریداری کردهاید)، میتوانید چرخش را در سطح برنامه پیادهسازی کنید. در محیط سرورلس از DynamoDB (AWS) یا KV Storage (Cloudflare) برای ذخیره وضعیت استفاده کنید:
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();
const PROXY_POOL = [
{ host: 'proxy1.example.com', port: 8080 },
{ host: 'proxy2.example.com', port: 8080 },
{ host: 'proxy3.example.com', port: 8080 }
];
async function getNextProxy() {
// دریافت ایندکس فعلی از DynamoDB
const result = await dynamodb.get({
TableName: 'ProxyRotation',
Key: { id: 'current_index' }
}).promise();
const currentIndex = result.Item?.index || 0;
const nextIndex = (currentIndex + 1) % PROXY_POOL.length;
// بهروزرسانی ایندکس
await dynamodb.put({
TableName: 'ProxyRotation',
Item: { id: 'current_index', index: nextIndex }
}).promise();
return PROXY_POOL[currentIndex];
}
exports.handler = async (event) => {
const proxy = await getNextProxy();
const response = await axios.get('https://api.example.com/data', {
proxy: {
...proxy,
auth: {
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS
}
}
});
return { statusCode: 200, body: JSON.stringify(response.data) };
};
این روش کنترل کاملی بر روی چرخش فراهم میکند، اما نیاز به درخواستهای اضافی به DynamoDB دارد (10-30 میلیثانیه تأخیر اضافه میکند). برای برنامههای با بار بالا، توصیه میشود که ایندکس را در حافظه کانتینر Lambda کش کنید و هر 100-1000 درخواست آن را بهروزرسانی کنید.
مدیریت خطاها و زمانهای تایم اوت
پروکسی یک نقطه شکست اضافی به برنامه سرورلس شما اضافه میکند. بسیار مهم است که خطاها را به درستی مدیریت کنید تا درخواستهای کاربران را از دست ندهید.
خطاهای متداول هنگام کار با پروکسی
| خطا | علت | راهحل |
|---|---|---|
| ETIMEDOUT | پروکسی پاسخ نمیدهد یا به آرامی کار میکند | زمان تایم اوت را به 5-8 ثانیه کاهش دهید، یک بار دیگر با پروکسی دیگر تلاش کنید |
| ECONNREFUSED | سرور پروکسی در دسترس نیست | دسترسی پروکسی را بررسی کنید، از پروکسی دیگری به عنوان پشتیبان استفاده کنید |
| 407 نیاز به احراز هویت پروکسی | اطلاعات ورود نادرست است | نام کاربری/کلمه عبور را بررسی کنید، اطمینان حاصل کنید که IP Lambda در لیست سفید پروکسی مجاز است |
| 502 Bad Gateway | پروکسی نمیتواند به وبسایت هدف متصل شود | سایت ممکن است پروکسی را مسدود کند، یک IP یا نوع پروکسی دیگر را امتحان کنید |
پیادهسازی منطق retry با پشتیبان
تلاشهای خودکار را با سوئیچ به پروکسی پشتیبان در صورت بروز خطاها اضافه کنید:
const axios = require('axios');
const PRIMARY_PROXY = {
host: 'primary.proxy.com',
port: 8080,
auth: { username: 'user', password: 'pass' }
};
const FALLBACK_PROXY = {
host: 'fallback.proxy.com',
port: 8080,
auth: { username: 'user', password: 'pass' }
};
async function fetchWithRetry(url, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const proxy = attempt === 0 ? PRIMARY_PROXY : FALLBACK_PROXY;
try {
const response = await axios.get(url, {
proxy,
timeout: 8000
});
return response.data;
} catch (error) {
console.log(`تلاش ${attempt + 1} ناموفق بود:`, error.message);
// در خطاهای مشتری (4xx) retry نکنید
if (error.response && error.response.status < 500) {
throw error;
}
// آخرین تلاش — خطا را پرتاب کنید
if (attempt === maxRetries - 1) {
throw error;
}
// تأخیر نمایی قبل از retry
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
}
}
}
exports.handler = async (event) => {
try {
const data = await fetchWithRetry('https://api.example.com/data');
return { statusCode: 200, body: JSON.stringify(data) };
} catch (error) {
return { statusCode: 500, body: JSON.stringify({ error: error.message }) };
}
};
این پیادهسازی سه تلاش برای درخواست را فراهم میکند: اولین بار از طریق پروکسی اصلی، و بقیه از طریق پروکسی پشتیبان. بین تلاشها تأخیر نمایی (1 ثانیه، 2 ثانیه، 4 ثانیه) اضافه شده است تا بار اضافی ایجاد نشود.
نظارت و هشدارها
نظارت بر خطاهای پروکسی را از طریق CloudWatch (AWS)، Vercel Analytics یا Sentry تنظیم کنید. معیارها را پیگیری کنید:
- درصد درخواستهای موفق از طریق پروکسی (باید >95% باشد)
- میانگین تأخیر درخواستها (افزایش میتواند نشاندهنده مشکلات با پروکسی باشد)
- تعداد خطاهای تایم اوت (اگر >5% — پروکسی بارگذاری شده یا کند است)
- توزیع خطاها بر اساس کدها (407، 502، ETIMEDOUT و غیره)
هشدارها را در صورت عبور از آستانهها تنظیم کنید — این امکان را فراهم میکند که به سرعت به پروکسی پشتیبان سوئیچ کنید یا پیکربندی را تغییر دهید.
نتیجهگیری
ادغام پروکسی در برنامههای سرورلس مشکلات حیاتی را حل میکند: مسدودیتهای IP مراکز داده، محدودیتهای نرخ و مسدودیتهای جغرافیایی. ما تنظیم پروکسی در AWS Lambda (Node.js و Python)، توابع Vercel و Cloudflare Workers را بررسی کردیم و همچنین پیادهسازی چرخش آدرسهای IP و مدیریت خطاها را بررسی کردیم.
توصیههای کلیدی: از پروکسیهای مسکونی برای وظایفی که نیاز به ناشناس بودن بالا دارند (استخراج دادهها، کار با APIهای شبکههای اجتماعی) استفاده کنید، اطلاعات ورود را در مخازن امن ذخیره کنید (AWS Secrets Manager، متغیرهای محیطی Vercel)، منطق retry را با پشتیبان پروکسی پیادهسازی کنید و نظارت بر خطاها را تنظیم کنید.
برای برنامههای سرورلس با نیازهای بالا به ثبات، توصیه میکنیم از پروکسیهای مسکونی با چرخش خودکار استفاده کنید — آنها تعادل بهینهای بین سرعت، ناشناس بودن و قابلیت اطمینان فراهم میکنند و خطر مسدودیتها را هنگام کار با APIها و خدمات خارجی به حداقل میرسانند.