You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何AWS Lambda使用requests.get请求无法解析域名时延迟10秒?

问题:AWS Lambda中requests请求无法解析域名时的10秒延迟问题

我在使用Python的requests库从AWS Lambda函数向无法解析的域名发起HTTP请求时遇到异常:

  • 执行代码:response = requests.get('https://benandjerry.com', timeout=(1,1))
  • 在Lambda环境中,请求需要约10秒才抛出错误,但本地环境报错即时
  • 已排除Lambda冷启动、运行时差异、VPC配置等问题;Lambda中用curl访问这些域名会即时返回Could not resolve host: benandjerry.com
  • 仅特定无法解析的域名出现该问题,例如:seen.ma、benandjerry.com、clenyabeauty.com、gong.com

可通过Python3.9版本的AWS Lambda复现,代码如下:

import json
from botocore.vendored import requests
import urllib.request
import os

def lambda_handler(event, context):
    url = 'http://benandjerry.com'
    try:
        response = requests.get(url, proxies=None,verify=False)
    except Exception as e:
        print(e)

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

疑问:

  1. 为何AWS Lambda使用requests.get请求无法解析的域名时会出现持续10秒的延迟?
  2. 如何让AWS Lambda像本地环境一样即时识别域名无法解析?

解答

1. 延迟原因

核心原因在于Lambda环境中requests的底层DNS解析逻辑和本地存在差异:

  • requests基于urllib3运行,而Lambda自带的botocore.vendored版本requests/urllib3,在遇到部分特殊域名(比如存在NS记录但无法返回A/AAAA记录的域名)时,会触发内置的重试逻辑或等待系统DNS resolver的默认超时,这个周期刚好约10秒。
  • 你设置的timeout=(1,1)仅控制连接和读取阶段的超时,不覆盖DNS解析阶段的超时,这就是为什么明明设置了1秒超时却还是会等待10秒的关键。
  • 而curl直接调用系统DNS解析逻辑,没有urllib3层的额外重试或等待,所以能即时返回解析失败的结果。

2. 解决方法

方法一:提前手动做DNS解析

用socket模块先单独解析域名,设置短超时,解析失败直接抛出异常,避免进入requests的等待流程:

import socket
import json
import requests
from urllib.parse import urlparse

def lambda_handler(event, context):
    url = 'http://benandjerry.com'
    parsed_url = urlparse(url)
    domain = parsed_url.netloc or parsed_url.path.split('/')[0]
    
    try:
        # 手动解析域名,设置1秒超时
        socket.setdefaulttimeout(1)
        socket.gethostbyname(domain)
        # 解析成功再发起请求
        response = requests.get(url, proxies=None, verify=False, timeout=(1,1))
    except socket.gaierror:
        print(f"无法解析域名:{domain}")
    except Exception as e:
        print(e)

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

方法二:替换为新版requests

botocore.vendored的requests版本老旧,DNS处理逻辑存在缺陷。可以通过Lambda层或部署包引入最新版requests和urllib3:

  1. 本地打包依赖:
    mkdir python
    pip install requests urllib3 -t python/
    zip -r requests_layer.zip python/
    
  2. 在AWS控制台创建Lambda层,上传上述zip包,并关联到目标Lambda函数
  3. 修改代码导入新版requests:
    import json
    import requests # 使用自己安装的新版本
    
    def lambda_handler(event, context):
        url = 'http://benandjerry.com'
        try:
            response = requests.get(url, proxies=None, verify=False, timeout=(1,1))
        except Exception as e:
            print(e)
    
        return {
            'statusCode': 200,
            'body': json.dumps('Hello from Lambda!')
        }
    

方法三:补丁urllib3的DNS解析逻辑

如果必须使用旧版requests,可以重写urllib3的连接逻辑,强制设置DNS解析超时:

import json
from botocore.vendored import requests
from botocore.vendored.requests.packages.urllib3.util.connection import create_connection
import socket

# 重写create_connection,添加DNS超时控制
def patched_create_connection(address, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None, socket_options=None):
    host, port = address
    try:
        # 设置DNS解析超时为1秒
        socket.setdefaulttimeout(1)
        ip = socket.gethostbyname(host)
    except socket.gaierror:
        raise
    return create_connection((ip, port), timeout, source_address, socket_options)

# 替换urllib3的默认实现
requests.packages.urllib3.util.connection.create_connection = patched_create_connection

def lambda_handler(event, context):
    url = 'http://benandjerry.com'
    try:
        response = requests.get(url, proxies=None, verify=False, timeout=(1,1))
    except Exception as e:
        print(e)

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

内容的提问来源于stack exchange,提问作者jjyoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 05:14:57