Bottle框架因响应生成卡顿求助:调用后端Python脚本异常
解决Bottle框架调用长耗时子进程导致响应崩溃的问题
嘿,你的问题根源确实在queryProcess.wait()这行代码上——这玩意儿会死死卡住当前请求的线程,直到子进程跑完才放行。哪怕你开了threaded=True,要是有几个耗时的请求同时进来,线程池很快就被占满,后续请求要么超时,要么看起来就像“崩溃”了一样。而且你还把wait()和communicate()一起用,这完全是画蛇添足,communicate()本身就会等待子进程结束并读取输出,wait()纯粹是多余的阻塞。
下面给你几个针对性的解决方案,从简单到进阶:
1. 先修复最基础的阻塞问题
直接删掉wait(),同时优化subprocess的调用方式(尽量别用shell=True,容易踩路径空格的坑):
from bottle import route, run, response, request import subprocess # 注意Windows路径用双反斜杠或者正斜杠,避免转义问题 sherlockQueryScriptPath = ".\\sherlock-backend\\Sherlock_Queries.py" emptyJsonString = "{}" def prepareJson(query): # 去掉shell=True,直接传参数列表,更安全 queryProcess = subprocess.Popen( ['python', sherlockQueryScriptPath, '-j', '-q', query], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True # 直接获取字符串,不用手动decode字节流 ) # 直接用communicate(),它会自动等待子进程完成 result, error = queryProcess.communicate() if error: print("Error: ", error) return emptyJsonString # 确保返回有效JSON,避免子进程输出空内容 return result.strip() or emptyJsonString @route('/your-endpoint') # 记得替换成你的实际路由 def handle_request(): query = request.query.get('q', '') # 假设从URL参数获取查询词 response.content_type = 'application/json' return prepareJson(query) def main(): run(host='localhost', port=8080, threaded=True) if __name__ == '__main__': main()
2. 进阶:用异步处理彻底解决线程阻塞
如果你的Sherlock_Queries.py运行时间真的很长(比如超过5秒),就算去掉wait(),单个请求还是会占用线程影响其他请求。这时候可以用Bottle的异步支持,搭配eventlet或者gunicorn这类异步服务器:
from bottle import route, run, response, request import asyncio sherlockQueryScriptPath = ".\\sherlock-backend\\Sherlock_Queries.py" emptyJsonString = "{}" async def run_sherlock_query(query): # 用asyncio的异步子进程API,不阻塞线程 proc = await asyncio.create_subprocess_exec( 'python', sherlockQueryScriptPath, '-j', '-q', query, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, text=True ) result, error = await proc.communicate() if error: print("Error: ", error) return emptyJsonString return result.strip() or emptyJsonString @route('/your-endpoint') async def handle_request(): query = request.query.get('q', '') if not query: response.content_type = 'application/json' return emptyJsonString result = await run_sherlock_query(query) response.content_type = 'application/json' return result def main(): # 用eventlet服务器支持异步,需要先pip install eventlet run(host='localhost', port=8080, server='eventlet') if __name__ == '__main__': main()
3. 额外的避坑建议
- 加超时保护:怕子进程卡死?给
communicate()加个超时时间,超时就杀掉进程:try: result, error = queryProcess.communicate(timeout=30) # 30秒超时 except subprocess.TimeoutExpired: queryProcess.kill() result, error = queryProcess.communicate() print("子进程超时被终止") return emptyJsonString - 用日志代替print:生产环境别用
print,换成logging模块,方便排查问题。 - 路径更靠谱:用
pathlib处理路径,避免相对路径的坑:from pathlib import Path sherlockQueryScriptPath = Path(__file__).parent / "sherlock-backend" / "Sherlock_Queries.py"
内容的提问来源于stack exchange,提问作者milkpirate
相关产品推荐
相关产品推荐

