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

使用Gunicorn Gevent Worker时Requests请求延迟过高的问题排查

Gevent Worker下Requests请求耗时异常问题排查

问题背景

我在对外提供HTTP接口的Flask应用中使用Gunicorn的Gevent Worker,接口执行流程中会调用另一个HTTP接口获取数据,且复用同一个requests.session。相关代码及测试数据如下:

rest_client.py代码

import requests
import json
import os
import time

class RestClient:
    base_url = None
    session = None

    def __init__(self):            
        if not RestClient.session:
            RestClient.base_url = f"http://localhost:5000"
            RestClient.session = requests.session()

    def fetch_http(self, key: str, name: str) -> str:
        fetch_http_endpoint = f"{self.base_url}/client/get-result"
        query_params = {"key": key, "name": name}

       
        session_get_start_time = time.time() * 1000
        response = self.session.get(fetch_http_endpoint, params=query_params)
        session_get_end_time = time.time() * 1000
        session_get_total_time = session_get_end_time - session_get_start_time
        print(f"session.get total time {session_get_total_time}")
        if response.status_code == 200:
            parsed_json = json.loads(response.content)
            result = parsed_json["result"]
            return result

gunicorn.py配置

PORT = os.getenv("PORT", 5000)
RELOAD = os.getenv("DEBUG", False)

bind = f"0.0.0.0:{PORT}"
workers = 1  # Only run one worker at a time, we deploy in k8s
worker_class = "gevent"  # Async workers
reload = RELOAD

测试耗时数据

Gevent Worker下session.get耗时:

session.get total time 28710.727783203125
session.get total time 23681.550048828125
session.get total time 18187.310302734375
session.get total time 21550.77294921875
session.get total time 20483.166259765625
session.get total time 10297.47705078125
session.get total time 5146.256103515625

同步Worker下session.get耗时:

session.get total time 41.621826171875
session.get total time 35.197998046875
session.get total time 24.538818359375
session.get total time 25.270751953125
session.get total time 21.31787109375
session.get total time 27.0400390625

版本信息

python==3.8
requests==2.24.0
gevent==21.1.2
gunicorn==20.1.0

我尝试过在导入Requests前显式猴子补丁但无效,Datadog Profiler显示热点在gevent/hub.py第166行的sleep函数,推测事件循环被阻塞,现寻求以下帮助:

  1. 使用Gevent Worker时出现该问题的原因是什么?
  2. 是否存在Requests未被猴子补丁的情况?如何验证?
  3. 为何同步Worker在该Demo中表现更优?

问题解答

1. Gevent Worker下请求耗时异常的原因

核心原因是Gevent事件循环被阻塞,结合热点日志分析,具体可能是:

  • 全局共享的RestClient.session在协程环境中存在竞态冲突,导致协程间出现无意义的等待;
  • 猴子补丁不完整,Requests底层的socket操作未被替换为Gevent的异步实现,同步IO直接阻塞了整个事件循环;
  • 单Gevent Worker处理并发请求时,阻塞IO会让所有协程排队等待,表现为请求耗时急剧上升。

2. 验证Requests是否被正确猴子补丁

可以通过以下方式验证:

  • 检查socket模块归属:在应用启动代码中添加打印,确认socket被Gevent替换:
    import socket
    print(socket.socket)  # 输出包含`gevent._socket3.socket`则说明补丁生效
    
  • 检查urllib3的socket实现:Requests依赖urllib3,查看其HTTPConnection使用的socket:
    from urllib3.connection import HTTPConnection
    print(HTTPConnection.socket)  # 需指向Gevent的socket实现
    
  • 强制前置补丁并验证:在应用入口最顶部执行补丁(确保在导入requests、socket前):
    from gevent import monkey
    monkey.patch_all()
    
    再运行上述检查代码,确认补丁生效状态。

3. 同步Worker表现更优的原因

同步Worker(默认sync worker)采用请求-线程/进程模型,每个请求的IO操作相互独立,不会互相阻塞。而你的测试场景中,单Gevent Worker补丁未生效时,所有并发请求的IO都是同步阻塞的,相当于单线程串行处理,一旦有请求触发IO等待,后续请求全部排队,导致总耗时飙升。同步Worker的线程模型下,每个请求的IO等待不影响其他请求,因此耗时保持正常水平。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 08:25:31