Web 后台任务管理
Web 后台任务管理
在 Web 开发中,经常会遇到这样的场景:用户点击一个按钮,背后需要执行一个耗时几秒甚至几分钟的操作,比如生成复杂报表、处理上传的视频、调用一个缓慢的第三方 API,或者运行数值求解器做大规模仿真。解决这类问题的核心思路是将耗时操作交给后台异步执行,让 Web 服务快速响应用户请求。为了达到这个目的,可以有多种方案,比如单线程同步执行、子进程异步执行、或者引入消息队列,通过中间件协调 Web 服务和后台任务进程等。这篇文章结合三个案例,从最简单的串行执行方式到基于 Redis + RQ 的任务队列,初步梳理后台任务管理的常见思路。这个文章暂时不涉及守护进程配置、任务结果持久化和后续任务控制等方面的内容,这些内容在后续的文章中整理。
项目准备
我们将构建一个极简的 Flask 应用,它有两个接口: * /task: 触发一个模拟的耗时任务。 * /health: 一个能立即返回的健康检查接口,用来检测我们的服务器是否“活着”。
我们创建一个简单的shell脚本来模拟一个需要10秒钟才能完成的任务。
long_task.sh:
1 |
|
在 Shell 脚本中,$$ 表示当前执行脚本或 Shell 的 PID(Process ID)。别忘了给它执行权限:chmod +x long_task.sh
案例一:同步阻塞
Werkzeug 实现
我们直接在API里调用任务并等待它完成。
1 | # case1_app.py |
在 Flask 开发服务器(Werkzeug)中,默认是使用多线程模式的。也就是说,即使某个请求处理过程中发生了阻塞(比如执行一个长时间运行的子进程),也不会阻塞整个服务器的进程,其他请求依然可以由其他线程并发处理,不会受到影响。例如,下面这个命令启动了一个默认的 Flask 开发服务器(开启了多线程):
1 | flask --app case1_app run --host=0.0.0.0 --port=9090 |
此时访问 /task 会同步执行脚本,但仍然可以同时访问 /health 获取立即响应。
为了演示阻塞对整个服务器的影响,我们可以强制关闭多线程模式,将服务器改成串行运行:
1 | flask --app case1_app run --host=0.0.0.0 --port=9090 --without-threads |
此时,在浏览器或 curl 中访问 http://127.0.0.1:9090/task,会发现这个请求“卡住”了10秒钟才返回结果。在卡住的这10秒内,如果访问 http://127.0.0.1:9090/health,会发现接口没有响应,直到 /task 执行完成,/health 才会返回 "OK"。这说明所有请求都被串行处理了,整个服务被阻塞。
1 | 收到创建任务请求,开始同步执行... |