生成器内部用try-except封装重试逻辑,通过指数退避(wait=min(2**retry_count,10))和retry_count控制重试间隔,成功后yield并重置计数,finally或GeneratorExit中确保session.close()等资源清理。

巧用生成器内部 try-except 配合重试逻辑,核心在于:**把重试控制权交给生成器自身,而非外部调用者**。这样 yield 任务就能在遇到网络抖动时主动“喘口气、再试一次”,而不是直接崩溃或抛出异常中断整个流程。
让生成器自己扛住瞬时失败
生成器天然适合封装带状态的长周期任务(如持续拉取日志、轮询接口、流式消费消息)。当某次 yield 前的网络请求失败,不急着退出,而是用内嵌的重试逻辑消化它:
- 在
yield前的业务逻辑块中包裹try-except,捕获requests.exceptions.ConnectionError、Timeout等瞬时异常 - 捕获后不
raise,而是执行退避等待(如time.sleep(1)),然后继续循环重试 - 成功获取数据后,才通过
yield向外输出结果
结合指数退避避免雪崩
单纯 sleep(1) 可能导致高并发下请求扎堆。更稳妥的做法是让每次重试间隔递增:
- 维护一个局部变量
retry_count,每次失败 +1 - 计算等待时间:
wait = min(2 ** retry_count, 10)(上限防失控) - 失败后
time.sleep(wait),然后continue回到循环开头 - 成功后重置
retry_count = 0,恢复轻量节奏
资源清理与优雅终止不可少
生成器可能被 gen.close() 或外部中断(如用户 Ctrl+C),此时必须确保连接、文件句柄等被释放:
- 用
try...finally包裹主循环,finally块中做清理(如session.close()) - 监听
GeneratorExit异常:在except GeneratorExit:中执行关键清理,再raise让其自然退出 - 避免在
except中静默吞掉GeneratorExit,否则资源泄漏风险极高
实际代码结构示意
以下是一个带重试、退避和清理的生成器骨架:
import time import requests <p>def polling_api(url, max_retries=5): session = requests.Session() retry_count = 0 try: while True: try: resp = session.get(url, timeout=3) resp.raise_for_status() data = resp.json() yield data # 成功才 yield retry_count = 0 # 重置计数 except (requests.ConnectionError, requests.Timeout, requests.HTTPError): if retry_count >= max_retries: raise # 超限则抛出,由外部决定是否兜底 wait = min(2 ** retry_count, 10) time.sleep(wait) retry_count += 1 finally: session.close() # 无论成功/失败/关闭,都释放连接

















