社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  tornado

[精华] 初识Tornado:一个简单例子

Py站长 • 11 年前 • 45189 次点击  

简单的web services:

现在我们将开始了解什么是tornado,tornado可以做什么。我们来通过分析tornado实现的一个简单web service 例子开始吧。

Hello Tornado

tornado 是一个可以处理http请求的框架,你的工作是作为一个程序员,编写一个handlers来响应一个标准的http请求。,下面是这个例子的所有代码:

范例1:hello.py

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)   
        self.write(greeting + , friendly user!’)

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的接口去定义一个类来扩展tornado 的RequestHandler类,在这个例子中,我们将会定义一个简单的应用来监听从端口获得的请求,并且返回一个响应值。

你可以通过命令行去试着运行和测试你自己的代码:

$ python hello.py –port=8000

现在你可以通过浏览器访问http://localhost:8000/,或者在另外一个独立的命令行窗口使用curl命令测试这个应用:

$ curl http://localhost:8000/   
Hello, friendly user!   
$ curl http://localhost:8000/?greeting=Salutations   
Salutations, friendly user!

让我们回到这个例子下,一步一步对它进行分析吧!

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

在程序的最上面,我们导入各种tornado的函数库,这里还包括更多的tornado函数库,但是在这个例子中你只需要导入四个函数库就可以运行了:

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

tornado集成了一些有用的函数在tornado.options里面,在这里我们使用这个函数库来完成监听HTTP请求的应用,这些参数将会对所有对象生效,如果你是在命令行中输入–help,你将会看到所有的帮助信息及其定义。如果你没有输入一个值,那么default变量后面的参数将会作为默认值开始执行,假如你输入的是一个错误的类型,程序将会抛出一个错误,在这里允许使用一个整数型数据作为监听的端口,如果没有输入任何数据,程序将会使用默认端口8000。

class IndexHandler(tornado.web.RequestHandler):   
    def get(self):   
        greeting = self.get_argument(‘greeting’, ’Hello’)   
        self.write(greeting + ’, friendly user!’)

这是一个tornado处理请求的类,当出现请求时,tornado实例将会调用这个类和方法。在这个例子中,我们只定义了一个get方法,这意味着程序只能处理HTTP GET的请求,在后面我们将会学习到如何使用更多接口去响应HTTP不同的请求。

greeting = self.get_argument(‘greeting’, ’Hello’)

tornado的请求类拥有很多有用的方法,比如get_argument,这个方法可以让我们获得请求字符串中的变量(如果请求中不包含字符串,tornado将会使用第二种方法替代get_argument,并使用默认值)

self.write(greeting + ', friendly user!')

第二个方法是使用一个请求的方法,当请求中有字符串变量时,我们将会获取这个字符串,并且插入一个问候语响应这条请求。

if __name__ == "__main__":   
    tornado.options.parse_command_line()   
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])

tornado的应用将会从这里开始执行,首先我们通过tornado的options函数库去解析命令,当我们创建一个tornado应用类的实例的时候,所有导入的变量将会传送到类的 __init__ 方法中,这个方法将会告诉tornado需要调用哪一个handle ,我们将会在后面对此做详细的说明。

http_server = tornado.httpserver.HTTPServer(app)   
http_server.listen(options.port)   
tornado.ioloop.IOLoop.instance().start()

程序将从这里开始执行,创建一个实例,我们通过应用tornado的HTTPServer对象来监听指定的端口在命令行中获取的数据,然后我们创建一个tornado的IOLoop实例,最后这个程序将会开始接收HTTP请求。

操作的变量

让我们再回头看看helly.py这个例子的第一行:

app = tornado.web.Application(handlers=[(r"/", IndexHandler)])

这个handlers非常重要,我们将会在后面对它进行更详细的解释,它是一个元组的列表,我们使用一个正则表达式将它与RequestHandler进行匹配,你可以添加更多的列表来指定更多需要匹配的RequestHandler

使用正则表达式来指定URL

tornado使用正则表达式来匹配URL的http请求(RequestHandler的类在URL后面,不包括主机名和查询字符串的片段),它们应该包含起始行和结束行。当一个正则表达式匹配一个HTTP请求时,匹配的内容将会作为参数传递给RequestHandler的类。我们将会在下一个例子了解到它是如何工作的。

字符串服务

我们将通过一个稍微复杂一些的例子1-2去介绍更多关于tornado的设计理念.

例1-2 Handling input: string_service.py

import textwrap  
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 ReverseHandler(tornado.web.RequestHandler):   
    def get(self, input):   
        self.write(input[::-1])

class WrapHandler(tornado.web.RequestHandler):   
    def post(self):   
        text = self.get_argument(‘text’)   
        width = self.get_argument(‘width’, 40)   
        self.write(textwrap.fill(text, width))

if __name__ == __main__“:   
    tornado.options.parse_command_line()   
    app = tornado.web.Application(   
        handlers=[   
            (r"/reverse/(\w+)", ReverseHandler),   
            (r"/wrap", WrapHandler)   
        ]   
    )   
    http_server = tornado.httpserver.HTTPServer(app)   
    http_server.listen(options.port)   
    tornado.ioloop.IOLoop.instance().start()

和第一个例子一样,你可以像这样在命令行中运行它

$ python string_service.py –port=8000

这个程序是一个用于处理字符串的基础web框架,现在你可以对它做两件事情。

第一:通过GET向 /reverse/string请求返回一个特定的字符串,类似这样的URL结构

$ curl http://localhost:8000/reverse/stressed   
desserts   
$ curl http://localhost:8000/reverse/slipup   
pupils

第二:通过POST向/wrap提交请求,并将返回的字符串显示出来。这个请求包括一个无长度限制的字符串,在程序中,我们通过get_argument来接收这个字符串变量。你可以这么测试:

$ curl http://localhost:8000/wrap >>   
-d text=Lorem+ipsum+dolor+sit+amet,+consectetuer+adipiscing+elit.

结果 :

Lorem ipsum dolor sit amet, consectetuer   
adipiscing elit.

这个字符串服务的例子大部分代码和上一个例子是相同的,让我们将注意力集中到那些新增的不同代码中来,首先让我们来看看程序中传送给handlers的变量有什么不同:

app = tornado.web.Application(handlers=[   
    (r"/reverse/(\w+)", ReverseHandler),   
    (r"/wrap", WrapHandler)   
])

在这个例子的代码中,我们可以看到有两个RequestHandlers实例化的类对Handlers进行处理,第一个tornado请求的地址按照下面的正则表达式进行匹配:

/reverse/(\w+)

这个正则表达式告诉tornado,他将会匹配一个/reverse/后面跟着一个或多个数字、字母的字符串,括号的含义是告诉tornado,需要将括号中匹配的字符串作为RequestHandler的请求参数,我们可以查看ReverseHandler的定义去了解它是如何工作的:

class ReverseHandler(tornado.web.RequestHandler):   
    def get(self, input):   
        self.write(input[::-1])

你可以看到这个类中,get方法需要输入一个额外的参数,这个参数是在第一个正则表达式中,完全匹配括号内的表达式的一个字符串,(假如有多个括号分割的正则表达式,可以按照相同的顺序在get中添加接收的参数的变量)

现在,让我们来看看WrapHandler的定义:

class WrapHandler(tornado.web.RequestHandler):   
    def post(self):   
        text = self.get_argument(‘text’)   
        width = self.get_argument(‘width’, 40)   
        self.write(textwrap.fill(text, width))

WrapHandler的类会处理从请求中传过来的字符串。我们可以看到这个类只定义了一个post的方法,这意味着它只能接收HTTP post的方法。我们之前是用过这个RequestHandler对象来抓取从请求中传过来的字符串作为变量。现在我们可以使用同样的方法去抓取POST请求传过来的变量(tornado可以解析POST传过来的URL字符串或参数),一旦我们抓取到了字符串及其长度的参数,就可以使用python内置的textwrap函数库库去转换字符串,并且通过HTTP返回生成的字符串作为响应数据。

更多RequestHandlers

到目前为止,我们基于RequestHandler开发这些对象:如何通过HTTP获取参数(使用get_argument 获取GET和POST传递过来的参数)和如何编写HTTP相应信息(使用write方法)。在我们开始后面的章节学习之前,再给你介绍一些应用RequestHandler需要注意的地方,以及tornado如何去使用它。

http方法:

截止目前讨论的都是关于RequestHandler定义单个HTTP方法的问题,其实在一个类中,可以定义或使用多个 handler,绑定多个功能到一个类中是一种很好的管理方式,你可能会编写一个处理程序去获取参数,并且通过POST和GET对象改变数据库中一个特定ID的值,例如:用一个GET方法中将抓取请求的ID和参数,并且使用POST方法修改数据库中对应ID的值。

截止目前我们讨论的例子中只使用了GET 和 POST的方法,实际上tornado还支持更多的HTTP方法(GET,POST,PUT,DELETE,HEAD,OPTIONS),在你编写的RequestHandler类中可以通过定义不同的方法去使用他们。在下面的虚拟案例中,我们将会使用HEAD方法去判断请求中的frob是否存在于数据库中,当存在时我们通过GET方法将frob对应的数据库信息返回给请求的客户端:

# matched with (r"/frob/(\d+)", FrobHandler)   
class FrobHandler(tornado.web.RequestHandler):   
    def head(self, frob_id):   
        frob = retrieve_from_db(frob_id)   
        if frob is not None:   
            self.set_status(200)   
        else:   
            self.set_status(404)   
    def get(self, frob_id):   
        frob = retrieve_from_db(frob_id)   
        self.write(frob.serialize())

HTTP状态码:

在上一个展示的例子中,你可以在你的RequestHandler使用set_status()设置HTTP状态码到客户端的相应信息中,下面是一些比较重要的状态码。tornado也可以根据你的相应自动去返回对应的状态码,在这里我们只列出比较常用的几个状态码:

  • 404 Not Found

如果HTTP请求的路径不存在,tornado将会通过RequestHandler类自动将404(Not Found)返回给客户端

  • 400 Bad Request

如果使用get_argument没有获取到默认的参数哦,或者没有找到定义的参数名,tornado将会自动将400(Bad Request)返回给客户端。

  • 405 Method Not Allowed

如果通过HTTP传进来的请求没有找到RequestHandler类中对应的方法(例如使用POST请求,但是handler方法对应的类却是GET方法)tornado将会返回一个405(Method Not Allowed)给客户端。

  • 500 Internal Server Error

当遇到任何应用服务异常退出的错误时,tornado将会返回500( Internal Server Error)给客户端,代码中任何意外退出都可能导致tornado返回一个500错误代码。

  • 200 OK

如果请求成功完成了,并且没有设置其它相应代码,tornado默认将会自动返回一个200(OK)的响应给客户端。

如果请求成功完成了,并且没有设置其它相应代码,tornado默认将会自动返回一个200(OK)的响应给客户端。

当出现一个错误出现时,tornado默认将会发送一个包含错误代码和错误信息的html页面给客户端,如果你想要替换默认响应的错误信息成自定义的页面,你可以在你的RequestHandler中重写write_error方法,例子1-3将会向你展示我们修改的hello.py,让你了解如何将初始化的错误信息重新改写。

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)   
        self.write(greeting + , friendly user!’)   
    def write_error(self, status_code, **kwargs):   
        self.write(Gosh darnit, user! You caused a %d error. % status_code)

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()

在当我们使用POST向handler发送请求时,将会出现下面的响应,因为我们已经把tornado默认的错误响应重写了:

$ curl -d foo=bar http://localhost:8000/   
Gosh darnit, user! You caused a 405 error.
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/158
 
45189 次点击  
文章 [ 7 ]  |  最新文章 10 年前
olivetree
Reply   •   1 楼
olivetree    10 年前

client = tornado.httpclient.AsyncHTTPClient() client.fetch("http://search.twitter.com/search.json?" + \ urllib.urlencode({"q": query, "result_type": "recent", "rpp": 100}), callback=self.on_response)

tornado里面可以这样实现异步,那么在请求返回之前,tornado在干什么?这个线程是什么状态?

Py站长
Reply   •   2 楼
Py站长    10 年前

@olivetree 异步是源码级的实现啦,这篇是简单的 hello world级别的实现。

NIO主要是在IO方面实现无阻塞化。

olivetree
Reply   •   3 楼
olivetree    10 年前

请问它是怎么实现异步的?这上面看不出来啊

Py站长
Reply   •   4 楼
Py站长    11 年前

@一休哥 都是好框架,各有好处~~

一休哥
Reply   •   5 楼
一休哥    11 年前

左手Django,右手Tornado,两手抓两手都要硬

Py站长
Reply   •   6 楼
Py站长    11 年前

@indexofire :)

indexofire
Reply   •   7 楼
indexofire    11 年前

写这么多真不容易!好的教程最好在wiki里都mark上