返回博客

如何在无服务器应用程序中设置代理:AWS Lambda、Vercel、Cloudflare Workers

完整的无服务器函数中代理集成指南:从设置HTTP客户端到绕过AWS Lambda、Vercel Edge Functions和Cloudflare Workers中的速率限制和地理封锁。

📅2026年2月19日
```html

无服务器架构已成为现代 Web 应用程序的标准,但开发人员经常面临一个问题:来自 Lambda 函数或 Edge Functions 的所有请求都来自云服务提供商数据中心的 IP 地址。这导致在访问外部 API、解析数据或自动化任务时出现阻止。在本指南中,我们将讨论如何将代理集成到无服务器函数中,以绕过限制、速率限制和地理封锁。

为什么无服务器函数需要代理

无服务器平台(AWS Lambda、Google Cloud Functions、Vercel、Cloudflare Workers)在云基础设施中执行代码,使用数据中心的 IP 地址。这为开发人员创造了几个关键问题:

问题 1: 数据中心 IP 阻止。 许多服务自动阻止来自已知 AWS、Google Cloud 或 Azure IP 地址的请求。例如,在解析电子商务网站(亚马逊、eBay、Wildberries)或社交网络(Instagram API、TikTok API)时,您的 Lambda 函数在第一次请求时就会收到 HTTP 403 或验证码。防止机器人系统(Cloudflare、Akamai、DataDome)会立即识别来自云数据中心的流量。

问题 2: IP 级别的速率限制。 如果您部署了一个具有数千个并发调用的无服务器应用程序,所有请求可能来自一个或多个 AWS IP 地址。外部 API 很快就会达到限制(例如,GitHub API — 每个 IP 每小时 60 个请求,Google Maps API — 每秒 100 个请求)。即使您已支付 API 的扩展计划,IP 限制仍然会生效。

问题 3: 地理封锁。 在 us-east-1 区域的无服务器函数无法访问仅在俄罗斯、欧洲或亚洲可用的内容。这在解析区域市场(Ozon、Yandex.Market)、检查来自不同国家的广告或测试网站本地化时至关重要。

问题 4: 与其他用户共享 IP。 在无服务器环境中,您的函数可能会获得一个已经被其他云服务提供商客户使用过的 IP 地址。如果有人之前滥用过这个 IP(垃圾邮件、DDoS、解析),它可能会被列入黑名单。您将会在没有任何过错的情况下被阻止。

解决这些问题的方法是集成代理服务器。代理允许您的无服务器函数通过看起来像普通用户的住宅或移动 IP 地址发送请求。这可以解除阻止、绕过速率限制并访问地理封锁的内容。

哪些类型的代理适合无服务器

选择代理类型取决于您无服务器应用程序的任务。我们来看看三种主要选项及其使用场景:

代理类型 速度 匿名性 使用场景
数据中心代理 非常高(10-50 毫秒) 无严格限制的 API 访问、服务可用性检查、正常运行时间监控
住宅代理 中等(100-500 毫秒) 电子商务解析、社交网络操作、绕过 Cloudflare、访问地理封锁内容
移动代理 中等(150-600 毫秒) 非常高 与移动 API(Instagram、TikTok)交互、移动应用测试、绕过最严格的保护

对于大多数无服务器应用程序,建议使用住宅代理。 它们在速度和匿名性之间提供了最佳平衡。住宅 IP 看起来像普通家庭用户,这使得绕过防止机器人和速率限制成为可能,而不会显著增加延迟。

数据中心代理仅适用于简单任务(检查 HTTP 状态、与没有限制的公共 API 交互)。移动代理在特定情况下需要使用——当您与移动 API 交互或对最大匿名性有严格要求时。

在 AWS Lambda 中设置代理

AWS Lambda 是最流行的无服务器平台,在这里集成代理需要正确配置 HTTP 客户端。Lambda 函数可以使用多种编程语言(Node.js、Python、Go),我们将讨论最常用的示例。

Node.js (axios)

Axios 是 Node.js 中最流行的 HTTP 请求库。要设置代理,请在配置中使用 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 中,使用带有 proxies 参数的 requests 库来处理代理:

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 客户端:

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 Functions(类似于 Lambda)和 Edge Functions(在 CDN 上执行)。Edge Functions 在接近 Cloudflare Workers 的运行时中工作,并对 Node.js API 的使用有限制。我们来看看这两种选择。

Vercel Node.js Functions

对于普通的 Vercel Functions,请使用与 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_HOSTPROXY_PORTPROXY_USERPROXY_PASS

Vercel Edge Functions

Edge Functions 使用 Web Fetch API,而不是 Node.js 库。代理通过自定义头或中间件进行设置:

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(需要填充)
  // 替代方案:直接使用代理 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 Functions 或在单独的服务器上创建中间代理服务器,该服务器将接受来自 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 });
}

此方法仅适用于支持 CONNECT 的 HTTP 代理。大多数住宅代理提供商提供此功能。

方法 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);

这种方法增加了额外的跳跃(增加 50-100 毫秒的延迟),但提供了对代理连接的完全兼容性和控制。

无服务器环境中的 IP 地址轮换

使用代理的主要原因之一是将请求分配到多个 IP 地址,以绕过速率限制。在无服务器架构中,有两种轮换方法:

代理提供商端的自动轮换

大多数住宅代理提供商提供旋转代理——您连接到一个端点,IP 会在每次请求时或通过指定间隔(例如,每 5 分钟)自动更改。这是无服务器的最简单选项:

// 一个端点,IP 自动更改
const proxyConfig = {
  host: 'rotating.proxy.example.com',
  port: 8080,
  auth: {
    username: 'user-session-' + Date.now(), // 唯一会话
    password: 'password'
  }
};

一些提供商允许通过用户名中的参数管理轮换: user-session-random(每次请求时更改 IP), user-session-sticky-300(在 300 秒内保持一个 IP)。

通过代理池手动轮换

如果您有静态代理列表(例如,您购买了专用代理),可以在应用程序级别实现轮换。在无服务器环境中,使用 DynamoDB(AWS)或 KV 存储(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 需要代理身份验证 凭据无效 检查用户名/密码,确保 Lambda 的 IP 被列入代理的白名单
502 错误网关 代理无法连接到目标网站 网站可能会阻止代理,尝试使用其他 IP 或代理类型

实现带有备用的重试逻辑

添加自动重试,错误时切换到备用代理:

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)进行重试
      if (error.response && error.response.status < 500) {
        throw error;
      }
      
      // 最后一次尝试——抛出错误
      if (attempt === maxRetries - 1) {
        throw error;
      }
      
      // 在重试前进行指数延迟
      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 Functions 和 Cloudflare Workers 中设置代理,以及实现 IP 地址轮换和错误处理。

关键建议:对于需要高匿名性的任务(解析、社交网络 API 操作),使用住宅代理,将凭据存储在安全存储中(AWS Secrets Manager、Vercel 环境变量),实现带有备用代理的重试逻辑,并设置错误监控。

对于对稳定性要求较高的无服务器应用程序,我们建议使用 住宅代理,并进行自动轮换——它们在速度、匿名性和可靠性之间提供了最佳平衡,最大限度地减少了在与外部 API 和服务交互时被阻止的风险。

```