线上大批量调用第三方接口时,固定线程池模式很容易被对方服务限流。核心原因就是请求特征太规整:线程标识、调用时序、请求上下文高度统一,完全是批量脚本特征。日常开发中主要靠线程特征隔离 和自适应流量整形两套方案解决,以下是落地代码和实操逻辑。
一、线程特征隔离(解决请求指纹统一问题)
python
import random
import time
import requests
# 浏览器指纹池
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
]
# 请求头模板
HEADER_PRESETS = [
{"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8"},
{"Accept": "application/json", "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8"},
{"Accept": "*/*", "Accept-Language": "zh-CN,zh;q=0.9"}
]
def create_scraper_session():
"""
创建独立的请求会话
每个线程调用时生成独立的 cookie jar 和 headers
"""
session = requests.Session()
# 随机选择一套请求头
headers = random.choice(HEADER_PRESETS).copy()
headers["User-Agent"] = random.choice(USER_AGENTS)
session.headers.update(headers)
# 随机延迟,避免请求时间过于规律
time.sleep(random.uniform(0.3, 1.8))
return session, headers
if __name__ == "__main__":
s, h = create_scraper_session()
print("Session headers:", dict(s.headers))
核心思路:每个工作线程全程独享独立请求特征、会话上下文,初始化做时序打散,打乱固定请求特征,规避机器批量调用识别。
实操要点:所有工作线程启动时单独调用该方法,不全局共享 Session 和 Headers。同时可随机微调 Header 字段顺序,进一步弱化标准化请求指纹。
二、自适应流量整形(解决固定 QPS 限流问题)
核心思路:摒弃恒定频率调用,模拟真人操作的间歇性特征,通过请求冷却、抖动退避,杜绝规整流量触发限流。
python
import random
import time
class RequestLimiter:
def __init__(self):
self.req_count = 0
def maybe_sleep(self):
"""偶尔歇会儿,别太频繁"""
self.req_count += 1
if self.req_count > random.randint(3, 6):
time.sleep(random.uniform(0.8, 2.5))
self.req_count = 0
def retry_with_backoff(self, func, retries=3):
"""出错时慢慢试,别急着重连"""
for attempt in range(retries):
try:
result = func()
self.maybe_sleep()
return result
except Exception as e:
if attempt == retries - 1:
print(f"挂了: {e}")
return None
wait = (2 ** attempt) * 0.5 + random.random()
print(f"重试中... {wait:.1f}s")
time.sleep(wait)
# 测试用
def test_req():
if random.random() < 0.6:
raise ConnectionError("timeout")
return f"ok {time.time():.0f}"
if __name__ == "__main__":
limiter = RequestLimiter()
for i in range(6):
print(f"\n第{i+1}次:")
r = limiter.retry_with_backoff(test_req)
print(r or "失败了")
实操要点:取消固定间隔调度,单线程自主控制请求节奏。失败请求不集中重试,通过抖动退避打散流量,避免流量共振引发批量限流。
落地总结
- 特征隔离:以线程为单位隔离所有请求标识和会话资源,通过时序抖动、随机报文配置,消除自动化批量调用的固定指纹
- 流量整形:打破恒定 QPS,通过分段冷却、抖动退避模拟自然调用节奏,大幅降低第三方服务限流命中率