Heroku接收3MB JSON POST请求时读取超时问题求助
3MB JSON POST请求在Heroku超时(H13错误)排查
问题情况
本地环境能正常处理包含3MB JSON字典的POST请求,但部署到Heroku服务器后,视图读取请求参数时出现超时,触发H13错误。
相关代码
请求发送代码
def send_request(): with open(file) as f: data = json.loads(f.read()) data = json.dumps(data) response = requests.post(url=url_recette, data=data) return response
服务器视图代码
@csrf_exempt def view(request): print('the server gets here') _args = json.loads(request.body.decode('utf-8')) print('the server never gets here so the timeout occurs when i load the args')
Heroku错误日志
Jan 13 09:00:03 gestion app[web] INFO [2023-01-13 09:00:03 +0100] [18] [INFO] Worker exiting (pid: 18) Jan 13 09:00:03 gestion heroku[router] error at=error code=H13 desc="Connection closed without response" method=POST path="/resiliation/api_get_requete_from_ak" host=gestion-recette.herokuapp.com request_id=xx fwd="xxxx.231" dyno=web.1 connect=0ms service=30826ms status=503 bytes=0 protocol=https Jan 13 09:00:04 gestion app[web] INFO [2023-01-13 09:00:04 +0100] [26] [INFO] Booting worker with pid: 26
原因分析
- Heroku超时限制触发:Heroku的Web Dyno默认请求处理超时时间是30秒,日志显示处理耗时30826ms(约30秒),刚好触碰到阈值,路由直接强制关闭连接,抛出H13错误。本地环境没有这个严格限制,所以能正常处理。
- 资源瓶颈:Heroku基础级Dyno的CPU和内存资源有限,3MB JSON的解析过程需要占用较多资源,服务器端资源不足时,解析速度远慢于本地,最终拖到超时。
- 一次性加载解析的低效:直接读取整个
request.body并一次性解析成JSON对象,会瞬间占用大量内存,不仅拖慢解析速度,还可能触发内存压力导致进程不稳定(日志里的worker重启也能佐证这点)。
解决方案建议
- 改用流式解析JSON:使用
ijson这类支持流式解析的库,分块处理大JSON数据,避免一次性加载全部内容到内存,降低资源占用和处理时间。示例代码大致如下:import ijson @csrf_exempt def view(request): print('the server gets here') # 流式解析request.body parser = ijson.parse(request.body) _args = {} for prefix, event, value in parser: # 根据实际JSON结构逐步构建数据 if prefix.endswith("your_key"): _args[prefix.split('.')[-1]] = value print('finished parsing') - 启用请求压缩:发送请求时对JSON数据进行gzip压缩,减少传输体积和服务器端的接收解析压力。修改发送代码:
同时确保服务器端配置支持自动解压gzip请求(Django可通过中间件实现)。import gzip from io import BytesIO def send_request(): with open(file) as f: data = json.loads(f.read()) data_bytes = json.dumps(data).encode('utf-8') # 压缩数据 buffer = BytesIO() with gzip.GzipFile(fileobj=buffer, mode='wb') as f: f.write(data_bytes) compressed_data = buffer.getvalue() # 发送压缩请求 response = requests.post( url=url_recette, data=compressed_data, headers={'Content-Encoding': 'gzip', 'Content-Type': 'application/json'} ) return response - 调整WSGI服务器超时:如果使用Gunicorn作为WSGI服务器,在Procfile中增加超时配置,比如:
给大请求处理留出更多时间,但注意Heroku的路由超时最长是55秒,不要超过这个值。web: gunicorn your_project.wsgi --timeout 60 - 升级Dyno规格:如果上述优化后仍无法解决,考虑升级到更高配置的Heroku Dyno,获得更多CPU和内存资源,提升大文件处理速度。
内容的提问来源于stack exchange,提问作者Arthur
相关产品推荐
相关产品推荐

