tornado入门

tornado是使用python编写的一个强大的,可扩展的web服务器。它在高并发的网络请求中表现的足够稳定,但是却在创建和编写的过程中有着足够的轻量级,并能够被用在大量的应用和工具中。
不同于那些最多只能达到10000个并发连接的传统web服务器,tornado在设计之初就考虑到了性能因素,旨在解决C10K问题,这样的设计使得其成为一个非常高性能的框架。此外,它还拥有处理安全性,用户验证,社交网络以及与外部服务进行异步交互的工具。

Hello tornado

tornado是一个编写对HTTP请求响应的框架。作为程序员,你的工作是编写响应特定条件HTTP请求的响应的handler。

hello.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 give port", type = int)
class indexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument("greeting", "hello")
self.write(greeting + ", friend 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的类。在这个例子中,我们创建了一个简单的应用,在指定的端口监听请求,并在根目录(“/“)下响应请求。
执行命令:

1
python hello.py --port=9999

使用命令对其访问

1
2
3
4
5
curl http://localhost:9999
hello, friend user!
curl http://localhost:9999/?greeting=david
david, friend user!

现在tornado程序就已经在localhost:9999处进行监听。我们可以对其进行根目录使用get方法进行那个访问.

程序分析

下面我们开始逐条语句进行分析:
开始的import语句导入了程序所必须的tornado的相关模块。虽然tornado还有其他模块,但是我们在这个程序中只需要这四个模块

tornado中options模块分析

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

tornado包含了一个有用的模块tornado.options来从命令行读取设置。我们在这里使用这个模块指定我们监听http请求的端口。它的工作流程是:
如果一个与define语句中同名的设置在命令行中给出,那么它将成为全局options的一个属性。如果用户程序在运行的时候使用了–help选项,程序将打印出你所定义的选项以及你在define函数的help参数中指定的文本。如果程序没有为这个选项指定值,那么则使用default进行替代。tornado使用type来验证参数的类型,当类型不合适的时候跑出一个异常。因此,我们允许一个整数port作为options.port来访问程序,如果用户没有指定port的值,则默认为9999端口。

请求处理函数类及方法

1
2
3
4
class indexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument("greeting", "hello")
self.write(greeting + ", friend user!")

这是tornado请求处理函数类,它继承自tornado.web.RequestHandler,当处理一个请求的时候,tornado将这个类实例化,并调用http请求方法所对应的方法。在这个例子中,我们只定义了get方法,也就是说这个类对http的GET请求作出响应。我们稍后将看到实现不止一个http方法的处理函数。

获取查询参数

1
self.get_argument("greeting", "hello")

tornado的request类有很多有用的内建函数,包括get_argument()用来用一个查询字符串中获取参数的值。上述的意思是从查询字符串中获取查询参数greeting的值,若查询参数中没有greeting参数,则会其值为”hello”,即为其默认值。

RequestHandler的write函数

1
self.write(greeting + ", firendly user")

tornado另一个有用的方法是write,它以一个字符串作为参数,并将其写入到http响应中去。在这里,我们使用请求中greeting的参数提供的值插入到greeting中,并写回到相应中去。

创建Application实例

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

这里是真正使得tornado运转起来的语句。首先我们使用tornado的options解析命令行。然后我们创建了一个tornado的Application实例。传递给Application的init方法的最重要的参数就是handlers。它告诉tornado用那个类来响应特定的请求。

端口监听

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

从这离开始的代码会被反复使用,一旦Application对象被创建,我们可以将其传递给httpServer的对象,然后使用我们在命令行指定的端口进行监听。最后,在程序准备好接收HTTP请求后,我们创建一个tornado的IOLoop的实例进行启动。

简单字符串服务

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
import textwrap
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.options
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, input1, input2):
self.write(input1)
self.write(input2)
class WrapHandler(tornado.web.RequestHandler):
def post(self):
text =self.get_argument("text")
width = self.get_argument("width", 40)
self.write(textwrap.fill(text, int(width)))
if __name__ == "__main__":
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/reverse/([0-9]+)(\w+)", ReverseHandler),\
(r"/wrap", WrapHandler)])
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.instance().start()

上述是一个简单的字符串操作的web服务,有两个作用:第一,到/reverse/string的GET请求奖会返回url路径中指定字符串的反转形式。

1
2
$curl http://localhost:8000/reverse/123456
654321

作用二是,到/wrap的POST请求从参数text中取得指定文本,并返回按照参数width指定宽度装饰的文本。下面的请求指定一个没有宽度的字符串,所以它的输出宽度被指定为程序中的get_argument的默认值40

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

RequestHandler的更多知识

HTTP方法

到目前为止我们只用了http中的get,post方法,但tornado支持任何合法的HTTP请求(GET,POST,HEAD,DELETE,OPTIONS)。你可以非常容易的定义上述任何一种方法的行为,只需要在RequestHandler类中使用同名方法。

HTTP状态码

从上面的代码可以看出,你可以使用RequestHandler类的set_status()方法显式的去设置http的状态麻,但是你需要记住在某些情况下,tornado会自动的设置状态码。下面是一种常见的纲要。

  • 404 Not Found
    当http请求的路径无法匹配到任何RequestHandler类对应的模式的时候tornado会返回404相应码。
  • 400 Bad Request
    如果你调用了一个没有默认值的get_argument()方法,并且没有发现给定名称的参数,tornado将自动返回一个400响应码
  • 405 Method Not Allowed
    如果传入的请求使用了RequestHandler中没有定义的http方法,tornado将自动返回一个405响应码。
  • 500 Internal Server Error
    当程序遇到任何不能让其退出的错误的时候,tornado自动返回响应码500.代码中任何没有捕获的异常也会返回500.
  • 200 OK
    如果相应成功,并没有其他返回码被设置,tornado将默认返回一个200.