使用Tornado处理请求超时
我在我的Tornado服务器上有一些比较长的请求需要处理。现在我刚开始了解Tornado的异步代码这个概念。看起来虽然这样可以让更多用户在服务器处理我这些长请求的时候与服务器互动,但这并不能解决我的问题,也就是请求超时。
异步代码在服务器上是异步运行的,但客户端还是同步的。这意味着虽然服务器可以处理更多的请求,但客户端会“卡住”,一直等到收到响应,直到超时(我说错了吗?)
我的问题是,如何解决浏览器的超时问题?换句话说,我想找一种方法,让客户端发出的请求能够立即返回,但请求的处理可以在后台进行。当处理完成后,应该以某种方式通知客户端。
有没有内置的机制或库可以做到这一点?处理这种情况的最佳方法是什么?
1 个回答
Tornado的RequestHandler可以接收客户端的POST请求,把任务放到一个队列里,然后回复“好的,我收到了”。
q = collections.deque()
class MyHandler(RequestHandler):
def post(self):
# Your code determines what work the client requests:
task_info = parse(self.request.body)
q.append(task_info)
self.finish(json.dumps({'ok': 1}))
客户端需要生成一个独特的任务ID,并把它包含在发送的信息中。
如果你想要一个更复杂的任务队列,可以看看我的Toro项目。或者,你也可以用Tornado的PeriodicCallback来定期检查队列里是否有任务,这样更简单。
那么,怎么让客户端知道任务完成了呢?如果客户端是浏览器,我建议使用websocket连接。这里有Tornado的websocket文档。当有人访问你的页面时,某段JavaScript代码应该打开一个到服务器的websocket连接。当访客提交一个任务时,JavaScript首先生成一个任务ID,订阅关于这个任务ID的通知,然后把任务信息POST到服务器。JavaScript代码如下:
var ws = new WebSocket("ws://localhost:8888/websocket");
ws.onmessage = function (evt) {
alert(evt.data);
};
/* Tell the server we want to know about this task */
function subscribe(task_id) {
ws.send(JSON.stringify({task_id: task_id}));
}
这是服务器的websocket处理程序:
notifiers = set()
class TaskNotifier(websocket.WebSocketHandler):
def open(self):
notifiers.add(self)
def on_message(self, message):
# Client is asking to subscribe to notifications about
# a certain task_id.
parsed = json.loads(message)
self.task_id = parsed['task_id']
def on_close(self):
notifiers.discard(self)
现在,每当你的代码从q
中取出一个任务并完成它时,代码会这样做(用Python):
for n in notifiers:
if n.task_id == task_id:
n.write_message('task %s done' % task_id)
然后,浏览器中的JavaScript客户端会检测到它等待的任务已经完成。
需要注意的是,客户端在实际提交任务到服务器之前就已经订阅了关于这个任务的通知;这样可以确保不会出现竞争条件,即使服务器很快就完成了任务。