• Tornado对Web请求与响应的处理机制

    作为Web Server的功能
    服务器君一共花费 10.000 ms 进行了 3 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    接下来我们看一下helloword.py的唯一一个handler。

    class MainHandler(tornado.web.RequestHandler): 
        def get(self): 
            self.write("Hello, world") 
    

    它是tornado.web.RequestHandler的一个子类,覆盖了父类的get方法。get方法也极简单,直接写一个“hello world”字符串到客户端。

    不难想到,Tornado在接到用户请求http://127.0.0.1:8888/时,最终会调用我们MainHandler的get方法。这中间经过了很多流程和逻辑,我们会一一跟踪并证实。

    接下来再看main函数的下一句。

    http_server = tornado.httpserver.HTTPServer(application)
    

    看起来我们是新建了一个http server的实例,前面创建好的application作为参数传递构了httpserver的构造函数。HTTPServer类定义在tornado/httpserver.py中。显然这是男主角。它的注释说明比Application还要长,需要重点关注。

    tornadoe中的httpserver的概念,简单概括下来就是:读取客户端的http request,调用对应的handler,然后用HTTPServer.write函数把数据返回给客户端。

    在注释中,作者举了一个最简单的例子来说明这个概念(甚至不需要用到Handler类的参与):

    import tornado.httpserver 
    import tornado.ioloop 
    
    def handle_request(request): 
        message = "You requested %s\n" % request.uri 
        request.write("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" % ( 
                       len(message), message)) 
        request.finish() 
    
        http_server = tornado.httpserver.HTTPServer(handle_request) 
        http_server.listen(8888) 
        tornado.ioloop.IOLoop.instance().start() 
    

    看到有多简单没有,一个handle_request函数就可以支撑起一个网站。当然,这个网站功能很简陋,不过是把客户的请求写回去而已。

    你当然可以在handler_request函数里大作文章,针对不同的url执行不同的代码,同样能达到前面Handler机制的效果,不过,tornado已经将这样的需求提炼出一套非常高效的handler机制,用起来也非常舒服。如果没有特别原因,就不必去“重复造轮子”的工作。

    这也是为什么我们一般称之为web framework,tornado已经把一个http server各流程的基础框架搭好了,你只需要填填空,客制化,“装修”一下。

    tornado 的HTTPServer会负责解析用户的HTTP Request,构造一个request对象。交给后面的RequestHandler处理。Request的解析是一个规范化的流程,基本不需要客制化。针对Request的处理函数(即RequestHandler)才是重点被客户化的部分。

    关于HTTPServer的分析将会占很大的篇幅,我们留在后面专门研究。在helloworld的分析中,我们只要记住,Application将会和HTTPServer实例绑定。

    http_server.listen(options.port)
    

    HTTP是工作在TCP协议上的,所以它其实是TCPServer的派生类。有过socket编程经验的读者都会记得,启动一个TCP服务器有三个必备步骤:

    1. create socket 创建socket。
    2. bind address 绑定地址。
    3. listen 执行侦听。

    TCPServer类的实现顺理成章的借鉴了Unix/Linux中socket的机制。所以它也存在上面的几个步骤,并且这几个步骤都是在HTTPServer.listen()函数调用时完成的。现在不会提这些细节,留在后面我们分析TCPServer这个类时再详查。

    listen函数的参数是端口号,前面提到,tornado demo的默认端口号都是8888,从helloworld.py的前面几行就可以看到。

    from tornado.options import define, options 
    define("port", default=8888, help="run on the given port", type=int) 
    

    define函数是OptionParser类的成员,定义在tornado/options.py中,机制与parse_command_line()类似。上面就是定义了一个int变量,名为port,默认值为8888,还附带一个说明文字,厉害。port变量会被存放进options对象的一个dictionary成员中。可以看到,访问方式就是options.port。执行完http_server.listen(options.port),你的PC上就会在options.port这个端口上启动一个服务器,开始侦听用户的连接。

    看完前面的http_server.listen(),似乎感觉少点什么。对了,没有掉到关键的accept函数,用户连接又怎么处理呢?别急,马上就是。这一句看上去很玄乎的东西,其实就相当于我们熟悉的accept。

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

    那为什么不直接来个accept?这个IOLoop又是什么东西?

    IOLoop与TCPServer之间的关系其实很简单。回忆用C语言写TCP服务器的情景,我们写好了create-bind-listen三段式后,其实事情还不算完。我们还得写点代码处理accept/recv/send呢。通常我们会写一个无限循环,不断调用accept来响应客户端链接。这个无限循环就是这里的IOLoop。这些代码让大家去写,最后其实都大同小异,因此tornado干脆写了一套标准代码,封装在IOLoop里。

    IOLoop会负责accept这一步。你可以注释掉IOLoop这一行,然后再访问http://127.0.0.1:8888/,就会发现根本连不上服务器。通过抓包会发现,实际上服务器并没有响应连接请求。

    对于recv/send操作,通常也是在一个循环中进行的,也可以抽象成IOLoop。我们分析HTTPServer类的实现时,会看到它是怎么借助IOLoop工作的。

    到此为止我们的web server已经完备了。

    我们在浏览器里输入http://127.0.0.1:8888/,浏览器就会连接到我们的服务器,把HTTP请求送到HTTPServer中。 HTTPServer会先parse request,然后将reqeust交给第一个匹配到的Handler。Handler负责组织数据,调用发送API把数据传到客户端。

    道理就是这样。

更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [PHP程序设计] PHP中的Hash算法 3 个条目
  2. [Python程序设计] Tornado源码解析 23 个条目
  3. [数据库技术] 数据库范式篇 5 个条目
  4. [Python程序设计] Python语言概述 6 个条目
  5. [PHP程序设计] CodeIgniter与PHP框架设计 5 个条目
  6. [PHP程序设计] 命令式编程范式 6 个条目
  7. [数据库技术] 无限级分类数据表设计 4 个条目
  8. [PHP程序设计] Nginx基本操作释疑 7 个条目
  9. [PHP程序设计] PHP数组的遍历 7 个条目
  10. [JavaScript程序设计] 关于HTTP Keep-Alive 6 个条目
  11. [Python程序设计] Python数据类型 11 个条目
  12. [Python程序设计] Django 入门知识浅介 10 个条目
窗口 -- [博客]