浅谈Tornado框架

0.714字数 3472阅读 2818

   tornado框架是一款相较于其他web framework处理服务器性能问题更加强健的轻量级的强大的Python web框架。tornado是一个可定制的非阻塞式异步加载框架,速度相当快。

大纲:

1,tornado入门

2,tornado表单和模板

3,模板扩展

4,数据库

5,异步web服务

6,编写安全应用

7,外部服务认证

8,部署tornado

tornado安装

     tornado的官网:http://www.tornadoweb.org/

        1,直接下载安装包并解压安装

         2,使用pip安装—>sudo pip install tornado

tornado简单的web服务

    Tornado是一个编写对HTTP请求响应的框架。下面是一个简单的Tornado应用的基础示例:

#引入需要的模块和包

import tornado.httpserver

import tornado.ioloop

import tornado.options

import tornado.web

from tornado.options import define, options

#定义段变量,这里默认为8000,可自己设置

define("port", default=8000, help="run on the given port", type=int)

#声明一个主页处理类,继承自RequestHandler

class IndexHandler(tornado.web.RequestHandler):

#重写父类的get方法,当请求为get方式时,执行此方法

def get(self):

greeting = self.get_argument('greeting', 'Hello')

#用write方法将需要的内容显示到网页上

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

#程序运行的入口

if __name__ == "__main__":

#得到所有的输入参数和参数值

tornado.options.parse_command_line()

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

#创建一个HTTP对象

http_server = tornado.httpserver.HTTPServer(app)

#监听接口

http_server.listen(options.port)

#创建一个I/O实例并启动

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

参数handler

    它是一个元组组成的列表,其中每个元组的第一个元素是一个用于匹配的正则表达式,第二个元素是一个RequestHanlder类。在hello.py中,我们只指定了一个正则表达式-RequestHanlder对,但你可以按你的需要指定任意多个。

表单和模板

Tornado自身提供了一个轻量级、快速并且灵活的模板语言在tornado.template模块中。

接下来是实例(写了比较重要的地方,其他的地方略去)

if __name__ == '__main__':

tornado.options.parse_command_line()

app = tornado.web.Application(

#路由映射

handlers=[(r'/', IndexHandler), (r'/poem', PoemPageHandler)],

#这里需要导入os模块配置模板路径,否则代码会报错

template_path=os.path.join(os.path.dirname(__file__), "templates")

)

http_server = tornado.httpserver.HTTPServer(app)

http_server.listen(options.port)

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

此时需要创建一个模板文件夹,然后在此文件夹下创建需要的HTML文件就可以加载到程序中。当然需要在后台调用渲染才可以出效果

模板语法:

        先给大家介绍一种简单的应用语法-->{{花括号里放需要渲染的变量或对象属性}}

tornado会通过Python解释器将模板语法解释出来

填充表达式:


控制流语句:

if选择结构:

{%if 条件%}

{%endif%}

for循环结构:

{%for i in 变量%}

{%endfor%}

模板函数:

Tornado在所有模板中默认提供了一些简单便利的函数。它们包括:

escape(s)

替换字符串s中的&、<、>为他们对应的HTML字符。

url_escape(s)

使用urllib.quote_plus替换字符串s中的字符为URL编码形式。

json_encode(val)

将val编码成JSON格式。(在系统底层,这是一个对json库的dumps函数的调用。查阅相关的文档以获得更多关于该函数接收和返回参数的信息。)

squeeze(s)

过滤字符串s,把连续的多个空白字符替换成一个空格。

设置静态文件

第一步先配置路径:

app = tornado.web.Application(

handlers=[(r'/', IndexHandler), (r'/poem', MungedPageHandler)],

template_path=os.path.join(os.path.dirname(__file__), "templates"),

#配置静态文件路径

static_path=os.path.join(os.path.dirname(__file__), "static"),

debug=True

)

static的子目录作为static_path的参数。现在应用将以读取static目录下的filename.ext来响应诸如/static/filename.ext的请求,并在响应的主体中返回。

static_url可以逆向生成文件的路径,如:

<link rel="stylesheet" href="static_url('style.css')">

这个对static_url的调用生成了URL的值,并渲染输出类似下面的代码:

<link rel="stylesheet" href="/static/style.css">

模板扩展

模板的继承与替换 

意义:简单,便捷,大大增加了代码的复用性,增加了代码的可读性

Tornado通过extends和block语句支持模板继承{% extends "filename.html" %}。

比如,为了在新模板中扩展一个父模板(在这里假设为main.html),你可以这样使用:

{% extends "main.html" %}

然后,为了在子模板index.html中覆写{% block header %}{% end %}部分,你可以使用块的名字引用,并把任何你想要的内容放到其中。

{% block header %}{% end %}

{% block header %}

<**>hello world<**>

{% end %}

任何继承这个模板的文件都可以包含它自己的{% block header %}和{% end %},然后把一些不同的东西加进去。

UI模块:

UI模块是封装模板中包含的标记、样式以及行为的可复用组件。它所定义的元素通常用于多个模板交叉复用或在同一个模板中重复使用。模块本身是一个继承自Tornado的UIModule类的简单Python类,并定义了一个render方法。当一个模板使用{% module Foo(...) %}标签引用一个模块时,Tornado的模板引擎调用模块的render方法,然后返回一个字符串来替换模板中的模块标签。UI模块也可以在渲染后的页面中嵌入自己的JavaScript和CSS文件,或指定额外包含的JavaScript或CSS文件。你可以定义可选的embedded_javascript、embedded_css、javascript_files和css_files方法来实现这一方法

如:app = tornado.web.Application(

handlers=[(r'/', HelloHandler)],

template_path=os.path.join(os.path.dirname(__file__), 'templates'),

ui_modules={'Hello': HelloModule}

)

此时在网页中写入{% module Hello() %}就会渲染为HelloModule

数据库:

torndb是一个轻量级的基于MySQLdb封装的一个模块,从tornado3.0版本以后,其已经作为一个独立模块发行了。torndb依赖于MySQLdb模块,因此,在使用torndb模块时,要保证系统中已经有MySQLdb模块。

torndb模块仅提供了数据库的连接和表项操作指令,没有提供数据库的创建等操作命令。所以需要自己定义数据库操作的增删改查

(1)连接数据库

import torndb

db=torndb.Connection(hostaddress,database name,user,password)

(2)以get方式查询一行数据

>>> a=db.get('select * from query where id=1')

>>> a

{'queryc': 'dac', 'id': 1}

(3)以query方式查询一列数据

>>> a=db.query('select * from query')

>>> a

[{'id': 2, 'queryc': 'isca'}, {'id': 1, 'queryc': 'dac'}]

(4)执行无返回参数的命令

string='dac'

str='insert into query(id,queryc)values(%d,"%s")'%(1,string)

db.execute(exe)

另外,torndb还提供了插入、插入多行数据的函数,实际这些函数并不必要,直接调用无返回参数的命令即可完成。

而且,torndb模块并并没有给MySQLdb模块增加额外功能,建议在python中使用mysql数据库时,直接使用MySQLdb模块即可。

异步web服务

大部分Web应用(包括我们之前的例子)都是阻塞性质的,Tornado给了我们更好的方法来处理这种情况。应用程序在等待第一个处理完成的过程中,让I/O循环打开以便服务于其他客户端,直到处理完成时启动一个请求并给予反馈,而不再是等待请求完成的过程中挂起进程Tornado包含一个AsyncHTTPClient类,可以执行异步HTTP请求。

例:

from tornado.web import Application, RequestHandler

from tornado.ioloop import IOLoopimport tornado.web

from tornado.httpclient import AsyncHTTPClient

import json

class IndexHandler(RequestHandler):

 # 通过装饰器注解的方式,告诉tornado,这个处理方式是异步操作~开发人员要通过代码手工控制返回响应 

 @tornado.web.asynchronous def get(self): 

 # 创建一个异步请求对象 

 client = AsyncHTTPClient()

 # 异步请求对象发送请求,获取数据 

 client.fetch("https://suggest.taobao.com/sug?code=utf-8&q=iphoneX",\ callback=self.deal_response) 

 def deal_response(self, response): 

 content = response.body 

 json_ = json.loads(content) 

 print(json_, type(json_)) 

 self.write("

异步ok!

")   

 self.finish()

if __name__ == "__main__":  

  app = Application([(r"/", IndexHandler), ]) 

   app.listen(8000) 

   IOLoop.current().start()

异步装饰器和finish方法:

Tornado默认在函数处理返回时关闭客户端的连接。在通常情况下,这正是你想要的。但是当我们处理一个需要回调函数的异步请求时,我们需要连接保持开启状态直到回调函数执行完毕。你可以在你想改变其行为的方法上面使用@tornado.web.asynchronous装饰器来告诉Tornado保持连接开启,记住当你使用@tornado.web.asynchonous装饰器时,Tornado永远不会自己关闭连接。你必须在你的RequestHandler对象中调用finish方法来显式地告诉Tornado关闭连接。(否则,请求将可能挂起,浏览器可能不会显示我们已经发送给客户端的数据。)我们在函数的write后面调用了finish方法表示完成

tornado长轮询:

我自己的理解:长轮询就是客户端发送一个ajax请求,如果客户端未更新数据,一直等待,若更新数据则返回响应,然后再发一个请求等待。

所谓的"服务器推送"技术允许Web应用实时发布更新,同时保持合理的资源使用以及确保可预知的扩展。对于一个可行的服务器推送技术而言,它必须在现有的浏览器上表现良好。最流行的技术是让浏览器发起连接来模拟服务器推送更新。这种方式的HTTP连接被称为长轮询或Comet请求。

长轮询意味着浏览器只需启动一个HTTP请求,其连接的服务器会有意保持开启。浏览器只需要等待更新可用时服务器"推送"响应。当服务器发送响应并关闭连接后,(或者浏览器端客户请求超时),客户端只需打开一个新的连接并等待下一个更新

tornado与websocket:

Tornado在websocket模块中提供了一个WebSocketHandler类。这个类提供了和已连接的客户端通信的WebSocket事件和方法的钩子。当一个新的WebSocket连接打开时,open方法被调用,而on_message和on_close方法分别在连接接收到新的消息和客户端关闭时被调用。

此外,WebSocketHandler类还提供了write_message方法用于向客户端发送消息,close方法用于关闭连接。

class EchoHandler(tornado.websocket.WebSocketHandler):

def open(self):

self.write_message('connected!')

def on_message(self, message):

self.write_message(message)

正如你在我们的EchoHandler实现中所看到的,open方法只是使用WebSocketHandler基类提供的write_message方法向客户端发送字符串"connected!"。每次处理程序从客户端接收到一个新的消息时调用on_message方法,我们的实现中将客户端提供的消息原样返回给客户端。

编写安全:

很多时候,安全应用是以牺牲复杂度(以及开发者的头痛)为代价的。Tornado Web服务器从设计之初就在安全方面有了很多考虑,使其能够更容易地防范那些常见的漏洞。安全cookies防止用户的本地状态被其浏览器中的恶意代码暗中修改。此外,浏览器cookies可以与HTTP请求参数值作比较来防范跨站请求伪造攻击。在本章中,我们将看到使防范这些漏洞更简单的Tornado功能,以及使用这些功能的一个用户验证示例。

6.1 Cookie漏洞

许多网站使用浏览器cookies来存储浏览器会话间的用户标识。这是一个简单而又被广泛兼容的方式来存储跨浏览器会话的持久状态。不幸的是,浏览器cookies容易受到一些常见的攻击。本节将展示Tornado是如何防止一个恶意脚本来篡改你应用存储的cookies的。

6.1.1 Cookie伪造

有很多方式可以在浏览器中截获cookies。JavaScript和Flash对于它们所执行的页面的域有读写cookies的权限。浏览器插件也可由编程方法访问这些数据。跨站脚本攻击可以利用这些访问来修改访客浏览器中cookies的值。

6.1.2 安全Cookies

Tornado的安全cookies使用加密签名来验证cookies的值没有被服务器软件以外的任何人修改过。因为一个恶意脚本并不知道安全密钥,所以它不能在应用不知情时修改cookies。

6.1.2.1 使用安全Cookies

Tornado的set_secure_cookie()和get_secure_cookie()函数发送和取得浏览器的cookies,以防范浏览器中的恶意修改。为了使用这些函数,你必须在应用的构造函数中指定cookie_secret参数

推荐阅读更多精彩内容