tornado学习总结(1)

这篇文章主要记录Tornado基本知识,以后知识点忘了可做查询用。Tornado大体上可以被分为4个主要部分:

  • web框架(包括创建web应用的RequestHandler类,还有很多其他支持的类)
  • HTTP客户端和服务端实现 (HTTPServer、AsyncHTTPClient)
  • 异步网络库(IOLoop、IOStream),为HTTP组件提供构建模块,也可以用来实现其他协议
  • 携程库(tornado.gen),允许异步代码写的更直接而不用链式回调的方式

基础知识

  1. 定义urls,并将urls绑定到处理类:

    app = tornado.web.Application(handlers=[(r"/",IndexHandler)])
    Application可以接受更多的配置项:

    settings=dict(
        template_path = os.path.join(os.path.dirname(__file__),"templates"),
        static_path = os.path.join(os.path.dirname(__file__),"statics"),
        debug = False,
        cookie_secret = base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes),              #用来使用get_secure_cookie方法
        xsrf_cookies = False,
        login_url = '/mockServer/login'
    )
    urls = [(r"/",IndexHandler)]
    application = tornado.web.Application(handlers=urls,**settings)  
    
  2. 绑定app(包含各种url信息)到http_server:

    http_server = tornado.httpserver.HTTPServer(app)

  3. 启动server监听(指定端口):

    http_server.listen(options.port)

  4. 加入到循环事务中,并启动事务:

    tornado.ioloop.IOLoop.instance().start()

  5. 处理类IndexHandler继承自tornado.web.RequestHandler,需要覆盖其get或post等方法。写内容到网页可以用: self.write(字符串),或者self.render(模版文件,key1=value1),模版文件中使用来引用对象,value1可以是list等复杂类型
  6. RequestHandler类有读写cookie的方法:

    set_cookie() / get_cookie()
    使用更安全的方式:set_secure_cookie() / get_secure_cookie() ,此时需要在Application的setting中增加如下设置:
    cookie_secret = base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
    set_secure_cookie中加上httponly=True, secure=True更加安全)
    清理指定cookie:clear_cookie('指定名称')

  7. 开启 XSRF 保护:

    a) 在Application的setting中增加如下设置:xsrf_cookies = True
    b) 在模板文件中的表单里面加入如下标记:

    {% raw xsrf_form_html () %}
    

    c) 在ajax中增加标记:

    function getCookie(name){
        var x = document.cookie.match("\\b" + name + "=([^;]*)\\b");
        return x ? x[1]:undefined;
    }
    
    $(document).ready(function(){
        $("#login").click(function(){
            var user = $("#username").val();
            var pwd = $("#password").val();
            var pd = {"username":user, "password":pwd, "_xsrf":getCookie("_xsrf")};
            $.ajax({
                type:"post",
                url:"/",
                async:true,
                data:pd,
                cache:false,
                success:function(data){
                    window.location.href = "/user?user="+data;
                },
                error:function(){
                    alert("error!");
                },
            });
        });
    });  
    
  8. json和python对象互相转换:

    tornado.escape.json_encode(python对象)==>json字符串
    tornado.escape.json_decode(json字符串)==>python对象
    它们与json 模块中的 dump()、load()功能相仿。

  9. 用户认证:

    a) 具体的Handler的get方法加装饰器:@tornado.web.authenticated
    b) 可以使用self.current_user变量获取当前用户
    c) a/b中的用法实际都会调用当前Handler类的get_current_user方法,所以前提是需要重载get_current_user方法,才可以使用。一般编写BaseHandler(tornado.web.RequestHandler)基类,里面实现get_current_user方法的重载,然后其他类继承自BaseHandler类
    d) 若当前用户不存在(即get_current_user返回None时),使用调用装饰器或者current_user变量的时候,会寻找Application里setting的login_url指定的路径

  10. 异步与阻塞:

    【异步阻塞】

    a) 设置异步函数结束后不断开服务器连接,使用`@tornado.web.asynchronous`装饰器。(原理是设置`_auto_finish`值为False,直到执行到`self.finish()`才关闭连接)  
    b) 使用`tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 17, callback=self.on_response)` 设置超时时间,以及所要执行的回调函数  
    C) 编写回调函数,并在函数最后加上`self.finish()`调用  
    
        class SleepHandler(BaseHandler):
            @tornado.web.asynchronous
            def get(self):
                tornado.ioloop.IOLoop.instance().add_timeout(time.time() + 17, callback=self.on_response)
            def on_response(self):
                self.render("sleep.html")
                self.finish()  
        =======另一种写法=========  
        class SleepHandler(tornado.web.RequestHandler):
            @tornado.gen.coroutine
            def get(self):
                yield tornado.gen.Task(tornado.ioloop.IOLoop.instance().add_timeout, time.time() + 17)
                #yield tornado.gen.sleep(17)
                self.render("sleep.html")
    

    【异步非阻塞】

    python2.7需要安装concurrent模块:`@tornado.concurrent.run_on_executor`  
    
    TIMEOUT = 30
    MAX_WORKERS = 50
    
    class BaseHandler(tornado.web.RequestHandler):
        executor = ThreadPoolExecutor(max_workers=MAX_WORKERS)  
    
  11. 异步函数编写:
    @tornado.gen.coroutine  
    def sleep(self):
        yield tornado.gen.sleep(10)
        raise tornado.gen.Return('hello world!')
    
  12. 完整实例:
    #coding=utf-8
    
    import tornado.httpserver
    import tornado.ioloop
    import tornado.options
    import tornado.web
    
    from tornado.options import define,options
    
    define("port",default=8000,help="run on the given port",type=int)
    
    class IndexHandler(tornado.web.RequestHandler):
        def get(self):
            greeting = self.get_argument('greeting','Hello')        #get_argument返回的是unicode编码
            self.write(greeting+',world!')
    
    if __name__=="__main__":
        tornado.options.parse_command_line()
        app = tornado.web.Application(handlers=[(r"/",IndexHandler)])
        http_server = tornado.httpserver.HTTPServer(app)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.instance().start()  
    

    tornado.httpserver:这个模块就是用来解决 web 服务器的 http 协议问题,它提供了不少属性方法,实现客户端和服务器端的互通。Tornado 的非阻塞、单线程的特点在这个模块中体现。
    tornado.ioloop:这个也非常重要,能够实现非阻塞 socket 循环,不能互通一次就结束呀。
    tornado.options:这是命令行解析模块,也常用到。执行终端help命令时出现的提示。
    tornado.web:这是必不可少的模块,它提供了一个简单的 Web 框架与异步功能,从而使其扩展到大量打开的连接,使其成为理想的长轮询。

  13. 模版:

    1) for循环:

    {% for i in key1 %}
        {{i[0]}}
    {% end %}
    

    2) 书写python表达式:
      使用双括号:7
    3) 调试模板:

    from tornado.template import Template
    print Template("{{ 'python'[0:2] }}").generate()    #输出py  
    

    4) 编写python语句:for,if,try,while等,可以多层嵌套

    {% 语句 %}
        ....
    {% end %}
    

    5) 设置变量:
    6) 对变量x不进行html转义:

    {% raw x %}  
    

    7) 常用模板函数:

    escape(s):替换字符串 s 中的 &、<、> 为他们对应的 HTML 字符。
    url_escape(s):使用 urllib.quote_plus 替换字符串 s 中的字符为 URL 编码形式。
    json_encode(val):将 val 编码成 JSON 格式。
    squeeze(s):过滤字符串 s,把连续的多个空白字符替换成一个空格。  
    
  14. 模板继承:

    1) 父模板:

    {% block 继承块名称 %}
        默认内容
    {% end %}
    

    2) 子模板:

    {% extends "base.html" %}
    {% block 继承块名称 %}
        更新内容
    {% end %}
    
  15. 模板组件:

    模板中引入其它模板:

    {% module Template("message.html", message=message) %}