关于Tornado asynchronous gen 的示例

Tornado 请求异步非阻塞在3.0 前后写法稍有不同,下面对Tornado 请求异步非阻塞作一下总结学习。

Tornado asynchronous gen 的示例

Tornado 3.0 后写法

3.0 以后是这个样子

1
2
3
4
5
6
7
8
9
10
class SleepHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self):
        yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 5)
        self.write("when i sleep 5s")

class JustNowHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("i hope just now see you")

asynchronous 写法

以前是这个样子

1
2
3
4
5
6
7
8
class SleepHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 5, callback=self.on_response)
    
    def on_response(self):
        self.write("when i sleep 5s")
        self.finish()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class SleepHandler(BaseHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self):
        # 这一行执行还是阻塞需要时间的,我的tt集合有一些数据并且没有索引
        cursor = db.tt.find().sort([('a', -1)])
        # 这部分会异步非阻塞的执行而不影响其他页面请求
        while (yield cursor.fetch_next):
            message = cursor.next_object()
            self.write('<li>%s</li>' % message['a'])
        self.write('</ul>')
        self.finish()

    def _on_response(self, message, error):
        if error:
            raise tornado.web.HTTPError(500, error)
        elif message:
            for i in message:
                self.write('<li>%s</li>' % i['a'])
        else:
            self.write('</ul>')
            self.finish()

另外一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from concurrent.futures import ThreadPoolExecutor
from functools import partial, wraps

import tornado.ioloop
import tornado.web

EXECUTOR = ThreadPoolExecutor(max_workers=4)

def unblock(f):

    @tornado.web.asynchronous
    @wraps(f)
    def wrapper(*args, **kwargs):
        self = args[0]

        def callback(future):
            self.write(future.result())
            self.finish()

        EXECUTOR.submit(
            partial(f, *args, **kwargs)
        ).add_done_callback(
            lambda future: tornado.ioloop.IOLoop.instance().add_callback(
                partial(callback, future)))

    return wrapper

class SleepHandler(tornado.web.RequestHandler):

    @unblock
    def get(self, n):
        time.sleep(float(n))
        return "Awake! %s" % time.time()

最常用的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import logging

import tornado.httpserver
import tornado.ioloop
import tornado.web

from tornado.httpclient import AsyncHTTPClient

class MainHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def get(self):
        http = tornado.httpclient.AsyncHTTPClient()
        http.fetch("http://localhost:8084/async-sync-test/", callback=self._test_callback)
        self.write("Hello to the Tornado world! ")
        '''
        Flushes the current output buffer to the network.
        '''
        self.flush()

    '''
    _test_callback is a callback function used for the processing of the response from the async request
    '''
    def _test_callback(self, response):
        self.write(response.body)
        '''
        refer the offical document, we are responsible for the invocation of the finish function in the async case.
        '''
        self.finish()

settings = {
    #"debug": True,
}

application = tornado.web.Application([
    (r"/", MainHandler),
], **settings)

if __name__ == "__main__":

    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8083)
    tornado.ioloop.IOLoop.instance().start()

官方的 asynchronous handler 示例

1
2
3
4
5
6
7
8
9
10
class AsyncHandler(RequestHandler):
    @asynchronous
    def get(self):
        http_client = AsyncHTTPClient()
        http_client.fetch("http://example.com",
                          callback=self.on_fetch)

    def on_fetch(self, response):
        do_something_with_response(response)
        self.render("template.html")

用 gen 来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class GenAsyncHandler(RequestHandler):
    @gen.coroutine
    def get(self):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch("http://example.com")
        do_something_with_response(response)
        self.render("template.html")

@gen.coroutine
def get(self):
    http_client = AsyncHTTPClient()
    response1, response2 = yield [http_client.fetch(url1),
                                  http_client.fetch(url2)]
    response_dict = yield dict(response3=http_client.fetch(url3),
                               response4=http_client.fetch(url4))
    response3 = response_dict['response3']
    response4 = response_dict['response4']

@gen.coroutine
def fetch_json(url):
    response = yield AsyncHTTPClient().fetch(url)
    raise gen.Return(json_decode(response.body))

非web 的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# -*- coding: utf-8 -*-
  
  import tornado.ioloop
  from tornado.httpclient import AsyncHTTPClient
  import functools
  
  def task(func, url):
      return functools.partial(func, url)
  
  def callback(gen, response):
      try:
          gen.send(response)
      except StopIteration:
          pass
  
  def sync(func):
      def wrapper():
          gen = func()
          f = gen.next()
          f(functools.partial(callback, gen))
      return wrapper
  
  @sync
  def fetch():
      response = yield task(AsyncHTTPClient().fetch, 'http://sohu.com')
      print '1'
      print response
      print '2'
  
  fetch()
  print '3'
  
  tornado.ioloop.IOLoop.instance().start()

使用了这个装饰器之后,你必须调用 self.finish() 已完成 HTTTP 请求,否则 用户的浏览器会一直处于等待服务器响应的状态:

1
2
3
4
5
class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.write("Hello, world")
        self.finish()

下面是一个使用 Tornado 内置的异步请求 HTTP 客户端去调用 FriendFeed 的 API 的例 子:

1
2
3
4
5
6
7
8
9
10
11
12
13
class MainHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        http = tornado.httpclient.AsyncHTTPClient()
        http.fetch("http://friendfeed-api.com/v2/feed/bret",
                   callback=self.on_response)

    def on_response(self, response):
        if response.error: raise tornado.web.HTTPError(500)
        json = tornado.escape.json_decode(response.body)
        self.write("Fetched " + str(len(json["entries"])) + " entries "
                   "from the FriendFeed API")
        self.finish()

例子中,当 get() 方法返回时,请求处理还没有完成。在 HTTP 客户端执行它的回 调函数 on_response() 时,从浏览器过来的请求仍然是存在的,只有在显式调用了 self.finish() 之后,才会把响应返回到浏览器。

关于更多异步请求的高级例子,可以参阅 demo 中的 chat 这个例子。它是一个使用 long polling 方式 的 AJAX 聊天室。如果你使用到了 long polling,你可能需要复写 on_connection_close(), 这样你可以在客户连接关闭以后做相关的清理动作。

本文网址: https://pylist.com/topic/59.html 转摘请注明来源

Suggested Topics

SAE+python+Tornado+pyTenjin 的完整示例

python 简单易懂,Tornado 高效易学,pyTenjin 轻巧快速,SAE 安全稳定使用门槛低。现在把他们结合在一起做了一个可运行在SAE 上的完整示例。...

tornado websocket 客户端与服务器端示例

最近在网上找了些websocket的资料看了下,node和tornado等等本身已经实现了websocket的封装,所以使用起来会比较简单,php如果想要写websocket还需要自己跑一整套流程,比较麻烦。...

Tornado 搭建基于 WebSocket 的聊天服务

这年头 Python web 框架是有点泛滥了. 下面要介绍的是 facebook 的开源框架 tornado. 这东西比较简单, 而且自带 WebSocket 支持, 可以用它做个简单的聊天室. ...

Tornado 构建一个 Comet 应用

Comet -- 基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推”技术为“Comet”,这里介绍用Tornado 构建一个 Comet 应用的经验。...

Leave a Comment