必须在程序开头执行gevent.monkey.patch_all()或至少patch_socket/ssl,否则requests等阻塞调用无法让出控制权;推荐gevent.pool.pool配合urllib3.poolmanager,显式设置超时、连接池大小,并异步化dns解析。

直接用 gevent 发 HTTP 请求,不加改造大概率会卡死或超时——因为默认没打补丁,requests 这类阻塞调用根本不会让出控制权。
必须在程序开头打 monkey patch
这是最常被跳过的一步。不打补丁,gevent 就看不到 socket、ssl、threading 等底层调用,所有请求仍按同步方式执行。
实操建议:
- 在
import任何可能触发网络/IO 的模块(如requests、urllib)之前,立即执行gevent.monkey.patch_all() - 若只用 HTTP,可缩小范围:
gevent.monkey.patch_socket()+gevent.monkey.patch_ssl(),更安全 - 避免在子模块里打补丁,必须在主入口文件最顶部完成
别直接用 requests —— 改用 gevent.pool + urllib3 或 grequests
requests 底层依赖 urllib3,而它默认用标准 socket;即使打了补丁,某些连接池行为仍可能引发竞争或泄漏。
实操建议:
- 推荐组合:
gevent.pool.Pool+ 原生urllib3.PoolManager(已兼容 greenlet) - 或使用轻量封装
grequests(注意它已多年未维护,仅适合简单 GET) - 避免在
Pool.spawn()里反复创建Session,应复用PoolManager实例 - 示例关键行:
pool = gevent.pool.Pool(20); pool.spawn(manager.request, 'GET', url)
超时、重试和连接池参数必须显式设
greenlet 不共享主线程的 timeout 机制,不设超时,一个慢请求会拖垮整个协程池;连接池若不限制大小,容易耗尽文件描述符。
实操建议:
- 给
urllib3.PoolManager设maxsize=10和num_pools=5,防止连接爆炸 - 每个请求必须带
timeout=gevent.Timeout(5),并在spawn外层用try/except gevent.Timeout捕获 - 重试逻辑不要依赖
requests.adapters.Retry,它在 greenlet 下行为不可靠;改用手动循环 +gevent.sleep() - 错误现象举例:
OSError: [Errno 24] Too many open files就是连接池失控的典型信号
真正难调的不是并发数,而是连接复用率和 DNS 解析——gevent 默认用系统 getaddrinfo,它是阻塞的。需要额外配 dnspython + gevent.resolver_thread 才能彻底异步化,这点绝大多数人会忽略。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











