Tornado Web框架

发布于 2017-09-07 15:40:21

刚学Python的时候就是学的Tornado框架。当时看的是《Introduction to Tornado》,从这里了解了web开发是个什么东西。

后来因为工作上没有用到这个框架,虽然尝试写个side project玩玩,却一再中止。这样的结果是,虽然用过,却连tornado框架的并发原理都没懂,只知道是一个异步的web框架。然而这是不严谨的,其实它由web框架加上web server两部分组成。

web框架的定义是(摘自维基百科)

Web应用框架(Web application framework)是一种电脑软件框架,用来支持动态网站、网络应用程序及网络服务的开发。这种框架有助于减轻网页开发时共通性活动的工作负荷,例如许多框架提供数据库访问接口、标准模板以及会话管理等,可提升代码的可再用性。

因为tornado的异步IO模型,它可以完成在高并发和长轮询、长连接这样的场景下的开发任务。前段时间写比特币比价程序的时候,就是用了tornado的websocket来实现“一次连接,双向通信”的效果。

然而,代码写了又忘,这篇文章希望对一些细节和思考做一些记录。

Tornado 应用结构

Tornado应用程序包含了一个或多个RequestHandler子类,一个Application实例对象,这个app对象按顺序进行正则匹配来完成路由功能,把请求导向到相应的handler中。

app的路由规则的第三个参数接受一个字典,会传给handler的initialize函数,对请求做一些初始化工作。而且路由规则可以包含一个name参数,来完成反向查询(reverse_url)的功能?

单个请求的顺序过程

  1. 创建RequestHandler对象
  2. 调用initialize()
  3. 调用prepare()
  4. 调用对应的http方法
  5. 调用on_finish()
    • 同步方法立即调用
    • 异步方法在finish()调用之后调用

Handler

一般会先定义一个BaseHandler的公共基础类,对每个请求会用到的功能做定义。例如write_error, get_current_user

Handler子类可以覆写get、post等http请求方法对应的函数。handler通过write、render来返回响应内容。render方法是用来渲染模板的,write方法可以返回字符串、字节流、字典。如果是字典的话,会被序列化为JSON。

  • 请求所有的数据都在self.request对象中
    • body
    • files 所有文件数据都是放在内存中的;是一个字典结构,每个字典包含了文件名、http content-type、文件体
  • get_query_argument(s)
  • get_body_argument(s)
  • 常覆写的方法
    • write_error输出错误页面html
    • on_connection_close 在客户端断开的时候被调用
    • get_current_user 用户验证相关
    • get_user_locale 返回Locale对象
    • set_default_headers 设置默认响应头

异步handler

两种

  • gen.coroutine装饰器:通过yield来暂停并恢复执行,直到函数返回(tornado推荐的方式)
    • Python3.5引入async def,在函数中通过await来暂停
    • 工作原理:coroutine装饰器通过yield和生成器交流,通过返回Future和协程调用者交流
    • 协程中的异常不会被察觉,一直被包裹在Future中,直到它yield,所以必须正确调用协程
    • 无需等待结果的后台执行
      • 使用IOLoop.spawn_callback
    • 协程中与其他代码交互的一些模式
      • 和回调交互

        • gen.Task包裹
        • yield gen.Task(some_function, other_args),这将返回一个Future对象
      • 和阻塞代码交互

        • 使用ThreadExecutor,返回一个和协程兼容的Futures
        thread_pool = ThreadPoolExecutor(4)
        @gen.coroutine
        def call_blocking():
            yield thread_pool.submit(blocking_func, args)
        
      • 并行

        • coroutine能够识别所有值为Future的列表或字典,并等待所有的异步执行完成
      • 交错执行

        • 在while循环中,通过yield手动解包异步代码返回的生成器,并flush至http响应中
      • 对生成器进行遍历

        • 需要将遍历条件和获取数据分开
      • 周期性回调

        • 在死循环中调用生成器函数,通过gen.sleep来休眠
        • 结合上面所说的IOLoop.spawn_callback
        • 精确周期调用(周期时间包含实际代码的执行时间)需要先gen.sleep,在执行其他代码,最后yield sleep返回的生成器
  • web.asynchronous装饰器:通过回调来异步,直到显式调用finish()方法
    • 特征:长连接,大部分时间是空闲的
    • tornado使用单线程事件循环模式
      • 同时只有一个操作在执行
      • 所有代码必须是异步非阻塞的
    • 阻塞和异步的区别
      • 阻塞(被调用者角度):等待事件发生,可能由网络IO,磁盘IO,锁导致。事实上,每个函数都可能或多或少阻塞,比如使用CPU加解密。某个函数可能在某方面阻塞,而在其他方面不阻塞。
      • 异步(调用者角度):函数在完成之前就返回,一般会产生一些后台工作,然后触发某些未来的动作
        • 一些接口类型
          • 定义回调参数
          • 返回一个占位对象
          • 发送给队列
          • 回调注册
        • 没有简单的方式使同步调用变成异步调用而同时对调用者透明
          • gevent使用了轻量级线程,并不是真正的异步执行

部署

  • debug模式
    • 自动reload
    • 禁用模板缓存
    • 禁用静态文件hash缓存
    • 启用错误栈跟踪
    • 不与HTTPServer的多线程模式兼容
  • 多核多进程模式
  • WSGI模式(tornado.wsgi.WSGIAdapter)不支持:
    • coroutine
    • asyncronous
    • AsyncHTTPClient
    • auth模块
    • WebSocket

协程和并发

tornado.queues

  • 类似于python标准库为多线程设计的queue模块
  • 可设置最大大小
  • 不是线程安全的,必须先转让控制权
    • 普通的队列Queue
    • 优先级队列PrioriryQueue(lowest first)
    • 后进先出队列LifoQueue

同步原语

  • tornado.locks
  • 用于协程间资源的同步,不是线程安全的
    • Condition:一个协程等待另一个协程的通知,可设置超时,超时抛出TimeoutError异常
    • Event:等待内部值被设置为True
    • Semaphore信号量:最多可被获取n次的“锁”,release将增加计数(无限制)
    • BoundedSemaphore:release不能使计数超过初始值
    • Lock:锁

Web框架

HTTP服务器和客户端

异步网络

IOLoop

非阻塞sockets事件循环

  • 单例IOLoop.instance
  • 也可计划基于时间的事件(.add_timeout)
  • 电平触发
  • 小节
    • 运行IOLoop
    • 回调和超时
    • 调试和错误处理
    • 子类方法

和其他服务交互

工具

  • autoreload
  • log
  • options
  • stack_context
  • testing
  • util
comments powered by Disqus