ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

requests 超时、重试、SSL 报错?9 个高频问题与一整张排错对照表

requests 超时、重试、SSL 报错?9 个高频问题与一整张排错对照表 requests 超时、重试、SSL 报错9 个高频问题与一整张排错对照表先给结论requests的默认配置几乎全是危险默认——默认没有超时、默认不重试、默认每次新建连接。绝大多数线上事故不是 requests 的 bug而是这三个默认没被覆盖。下面 9 个坑按线上出现频率排序每个都给现象、根因、修法。坑 1不设 timeout请求永远挂着现象程序卡死几小时日志没有任何报错线程/协程全部耗尽。根因requests的timeout默认值是None即无限等待。服务端不返回客户端就一直等着。# 危险没有超时rrequests.get(url)# 正确分别设置连接超时和读取超时rrequests.get(url,timeout(3.05,10))timeout(3.05, 10)表示连接超时 3.05 秒、读取超时 10 秒。这两个数是不同的东西连接超时是 TCP 握手读取超时是连上之后等多久收到下一个字节。只传一个数字timeout5则表示两者都用 5 秒。坑 2以为 requests 会自动重试它不会现象偶发的 502/连接重置直接抛异常明明重试一次就好。根因requests 本身不做任何重试。重试要挂urllib3的Retry到HTTPAdapter上。fromrequests.adaptersimportHTTPAdapterfromurllib3.util.retryimportRetry retryRetry(total3,# 总重试次数backoff_factor0.5,# 退避0.5s, 1s, 2sstatus_forcelist[429,500,502,503,504],allowed_methods[GET,HEAD],# 只对幂等方法重试)srequests.Session()s.mount(https://,HTTPAdapter(max_retriesretry))注意allowed_methods默认只重试幂等方法。如果你的重试列表里放了 POST重复下单这类非幂等请求会出大事故。坑 3verifyFalse一关了之现象SSLError: certificate verify failed加verifyFalse后 warning 刷屏。根因证书链校验失败内网自签证书、系统 CA 过旧、代理中间人证书。verifyFalse只是关掉校验等于放弃了中间人攻击防护urllib3 会持续打InsecureRequestWarning。修法按优先级用certifi的最新 CArequests.get(url, verifycertifi.where())内网自签证书把 CA 证书路径传给verify/path/to/ca.pem仅在本机调试时临时verifyFalse并配urllib3.disable_warnings()坑 4每次都requests.get连接池白建现象QPS 上不去TIME_WAIT 堆积。根因requests.get()每次都新建一个Session握手、DNS、TLS 全部重来。# 慢每次新建连接foruinurls:requests.get(u)# 快复用 Session自动连接池 keep-alivewithrequests.Session()ass:foruinurls:s.get(u,timeout(3,10))坑 5Session 跨线程共享现象多线程下偶发莫名其妙的连接错误、响应串号。根因Session不是线程安全的。它内部的连接池虽然有一定并发能力但 cookies、适配器状态并非为并发设计。修法每个线程一个 Session或用threading.local()存或改用httpx明确区分 Client 的线程/异步模型。坑 6中文乱码现象response.text出现æ\u0088\ue105之类乱码。根因服务器返回的Content-Type没带charset时requests 按 HTTP 默认ISO-8859-1猜text就按错编码解。rrequests.get(url)# 先看它猜成了什么print(r.encoding,r.apparent_encoding)# ISO-8859-1 vs utf-8r.encodingr.apparent_encoding# 或 r.encoding utf-8print(r.text)判据r.encoding取自响应头r.apparent_encoding是chardet/charset_normalizer实际探测的结果。两者不一致时以探测结果为准。坑 7data和json分不清现象后端收不到参数或收到的是一坨字符串。写法Content-Type请求体data{a:1}application/x-www-form-urlencodeda1json{a:1}application/json{a: 1}datajson.dumps(d)仍是 form 表单除非手动加头字符串被当表单值想要 JSON 就直接用json参数手工datajson.dumps(...)必须自己补headers{Content-Type: application/json}。坑 8下载大文件把内存打爆现象下载几百 MB 文件内存飙到几 GB。根因response.content会一次性把整个响应体读进内存。withrequests.get(url,streamTrue,timeout(3,30))asr:r.raise_for_status()withopen(big.zip,wb)asf:forchunkinr.iter_content(chunk_size8192):ifchunk:f.write(chunk)streamTrue之后必须消费或关闭响应否则连接不会归还连接池见坑 9。坑 9响应没关连接池耗尽现象跑一段时间报ConnectionError: Max retries exceeded或urllib3提示连接池已满。根因拿到响应后只取了status_code没读 body或用streamTrue后没读完也没close()连接就一直占着。rrequests.get(url,streamTrue)r.close()# 显式归还# 更好的写法withrequests.get(url,streamTrue)asr:...一整张排错对照表现象根因修法程序永久挂起timeout默认None始终传timeout(3, 10)偶发 502 没人重试requests 不自动重试RetryHTTPAdapterSSLError/ warning 刷屏证书链校验失败verifycertifi.where()或指定 CAQPS 上不去每次新建连接复用Session多线程偶发串号Session 非线程安全每线程一个 Session中文乱码默认ISO-8859-1设r.encoding r.apparent_encoding后端收不到 JSONdata发成表单用json下载大文件 OOMcontent全读进内存streamTrueiter_content连接池耗尽响应未关闭with或显式close()最后requests 的三个危险默认无超时、不重试、不复用连接。把它们覆盖掉能消掉线上八成以上的 HTTP 相关故障。再配一条所有响应都用with包住别让连接泄漏。你在 requests 上踩过最坑的一次是什么评论区说说。
返回列表