Tornado 特点

Tornado是一个用Python写的相对简单的、不设障碍的Web服务器架构,用以处理上万的同时的连接口,让实时的Web服务通畅起来。虽然跟现在的一些用Python写的Web架构相似,比如Django,但Tornado更注重速度,能够处理海量的同时发生的流量。 
FriendFeed的联合创始人Bret Taylor的博客里介绍了更多,他说:把Tornado开源,FriendFeed和Facebook期望第三方能够用以建筑实时的Web服务。其具体的工作原理如上图,看起来很像是FriendFeed的评论系统。

Taylor认为Tornado的三个关键部分是: 
         完整的用以构建网站的基础模块。Tornado包含内置的用以解决网络开发最难和最烦的功能模块,包括模板、signed cookies、用户认证、地方化(localization)、aggressive static file caching, cross-site request forgery protection,以及类似Facebook Connect的第三方认证。开发者可以随取所需,并且自由组合,甚至把Tornado与其他架构组合。 
实时服务。Tornado支持大量的同时发生的信息连接。用Tornado,能够通过HTTP或者Long Polling方便的书写实时服务。要知道,每一个FriendFeed的活跃用户都保持有一个连通FriendFeed服务器的开放通路。 
高效能。Tornado比大多数用Python写的Web架构更快。根据一些实验,Tornado的速度是一般架构的4倍。

Tornado 支持二种模式 一种是串行访问处理,异步非阻塞。当客户端访问web,tornado 可以自行定义前面2个的处理方式。天生支持RESTful

安装

pip install tornado

官方:http://www.tornadoweb.org/en/stable/

一 、HELLO word

 import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world") def make_app():
return tornado.web.Application([
(r"/index", MainHandler),
]) if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

访问 ip:8888/index 测试内容

配置模板

返回html页面

#_*_coding:utf-8_*_
import tornado.ioloop
import tornado.web #通过字典定义的方式定义配置文件属性,
settings = {
'template_path':'template',
} class MainHandler(tornado.web.RequestHandler):
def get(self):
#self.write("Hello, world")
#返回模板中的页面
self.render('index.html') def make_app():
#把setting这个配置参数带入到方法中
return tornado.web.Application([
(r"/index", MainHandler),],**settings) if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

然后就可以测试页面了,非常的简单

这里介绍render都做了什么
class MainHandler(tornado.web.RequestHandler):
               def get(self):

1先找到template的路径

2根据路径找到对应的html页面(拼接)

3python open方法读取这个html页面内容,将内容读取到了内存中。实际上读出来的其实就是一个字符串

4 self.write(open('html').read()) 读出这个文件然后写回(self.write方法)

5self.write用socket的send方法,将字符串发送给客户端用户(这里会将这个字符串塞进一个双向队列里面,send方法回去队列中去数据然后发送给客户端),用户就可以看见web页面了(这个就是一个简单的过程)

dic ={ 'name'='aaaa' }

这里在说一下模板渲染,上面定义了一个字典dic。那过程就需要多加一步了。

在上面4步:

4open('html').read() 先把文件读取出来

5渲染html页面(实际就是一个很长的字符串),替换字符串中的特殊模板语言(在html文件中也会有模板语言{{ name }},这个key可以dic中的key对应上)

6self.write

self.render('index.html')

#self.render('index.html',**dic)  渲染必须将字典当参数一起传递

路由系统

#_*_coding:utf-8_*_
import tornado.ioloop
import tornado.web #通过字典定义的方式定义配置文件属性,
settings = {
'template_path':'template',
} class MainHandler(tornado.web.RequestHandler):
def get(self):
dic={
'name':'abc'
}
self.render('index.html',**dic) class Home(tornado.web.RequestHandler):
def get(self):
self.render('home.html') class news(tornado.web.RequestHandler):
#接受从url传递过来的参数,nid是从url中传递过来的
def get(self,nid):
self.write(str(nid))
'''
(r"/news/(\d+)",news),这里也可以用正则匹配.想要当参数传递必须()中写表达式
'''
def make_app():
#把setting这个配置参数带入到方法中
return tornado.web.Application([
(r"/index", MainHandler),
(r"/home",Home),
(r"/news/(\d+)",news),
],**settings) if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

匹配二级域名,这个是Tornado独有的,一般都是匹配xxx.xxxx.xxx/后面的参数,现在要匹配 psp.xxx.com/index  ,匹配psp 这个前缀

#_*_coding:utf-8_*_
import tornado.ioloop
import tornado.web #通过字典定义的方式定义配置文件属性,
settings = {
'template_path':'template',
} class MainHandler(tornado.web.RequestHandler):
def get(self):
dic={
'name':'abc'
}
self.render('index.html',**dic) class psphandler(tornado.web.RequestHandler):
def get(self):
self.write("psp.tb.com/index")
app=tornado.web.Application([
(r"/index", MainHandler),
],**settings) #对app的add_handlers方法在增加一个匹配项,如果匹配上就掉对应的方法。当url过来的时候如果psp.tb.com匹配不上还是回去其它定义的规则中继续匹配
app.add_handlers('psp.tb.com',[
(r"/index",psphandler),
]) if __name__ == "__main__":
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

测试结果:

路由psp

路由正常规则

静态文件

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>index.html</h1>
<h1>{{ name }}</h1>
</body>
</html>

index.html

 #_*_coding:utf-8_*_
import tornado.ioloop
import tornado.web '''
必须定义'static_path':'static',否则'static_path_prefix':'/static/',会报错误。
现在不定义'static_path_prefix':'/static/',前端html文件"{{static_url("commons.css")}}" 也会对文件添加md5码。
当文件更新的时候就会非常方便了,客户端也可以发现静态文件更新,再也不用手动强制更新ie缓存了
'''
settings = {
'template_path':'template',
'static_path':'static',
'static_path_prefix':'/static/',
} class MainHandler(tornado.web.RequestHandler):
def get(self):
dic={
'name':'abc'
}
self.render('index.html',**dic)
app=tornado.web.Application([
(r"/index", MainHandler),
],**settings) if __name__ == "__main__":
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

访问展示:

主要看静态文件后面的自己生成的md5值,然后自己改一下样式重启服务器,客户端刷新就会直接刷新缓存了。

这里还有一个好处是前端不用管后端static文件夹的改变,后端更改路径不会影响前端

cookie操作

#_*_coding:utf-8_*_
import tornado.ioloop
import tornado.web #cookie_secret这个配置的是加"盐"的值
settings = {
'template_path':'template',
'static_path':'static',
'static_path_prefix':'/static/',
'cookie_secret':'asdasdasda',
}
class HomeHandler(tornado.web.RequestHandler):
def get(self):
#读取客户端cookie,k1就是客户端cookie的一个字符串
#self.set_cookie('k1') #设置cookie (参数):name, value, domain=None, expires=None, path="/",expires_days=None, **kwargs
#path="/" 这个参数是设置cookie的作用域范围。www.xxx.com/home 如果这样写就说明这个cookie只作用在home这个url上别的url无法使用
#set_cookie是不加密的 直接明文就传递给客户端,可以用调试工具查看到
self.set_cookie('name','abc')
#加密cookie 进行签名
#self.set_secure_cookie('name','123')
self.render('home.html') def make_app():
return tornado.web.Application([
(r"/home", HomeHandler),
],**settings) if __name__ == "__main__":
app = make_app()
app.listen(8888)
tornado.ioloop.IOLoop.current().start()

演示:

因为这里无法同时开启self.set_cookie和self.set_secure_cookie。所以这个实验是执行完一个在换另一个。加密的还是相对比较安全的 。

看源码是如何处理加密:

源码:

def set_secure_cookie(self, name, value, expires_days=30, version=None,
**kwargs):
# 这里默认将超时时间设置了expires_days=30,注意看它这直接调用了不加密码的cookie方法self.set_cookie()。
#self.create_signed_value(name, value,ersion=version)加密方法
self.set_cookie(name, self.create_signed_value(name, value,ersion=version),xpires_days=expires_days, **kwargs)

create_signed_value加密方法

def create_signed_value(self, name, value, version=None):
#获取加盐的方法self.require_setting("cookie_secret", "secure cookies")
#在前面的setting增加这几个key就可以
self.require_setting("cookie_secret", "secure cookies") secret = self.application.settings["cookie_secret"]
key_version = None
if isinstance(secret, dict):
if self.application.settings.get("key_version") is None:
raise Exception("key_version setting must be used for secret_key dicts")
key_version = self.application.settings["key_version"]
#这个是全局的变量,虽然跟这个类中的方法名字相同。但是这个不是传给自己的 create_signed_value
return create_signed_value(secret, name, value, version=version,
key_version=key_version)

create_signed_value

def create_signed_value(secret, name, value, version=None, clock=None,
key_version=None):
if version is None:
version = DEFAULT_SIGNED_VALUE_VERSION
if clock is None:
clock = time.time
#生成时间戳
timestamp = utf8(str(int(clock())))
#用base64.b64encode加密,base64是可以反解回来的
value = base64.b64encode(utf8(value))
#有2个版本加密一个是v1一个v2
if version == 1:
signature = _create_signature_v1(secret, name, value, timestamp)
value = b"|".join([value, timestamp, signature])
return value
elif version == 2:
# The v2 format consists of a version number and a series of
# length-prefixed fields "%d:%s", the last of which is a
# signature, all separated by pipes. All numbers are in
# decimal format with no leading zeros. The signature is an
# HMAC-SHA256 of the whole string up to that point, including
# the final pipe.
#
# The fields are:
# - format version (i.e. 2; no length prefix)
# - key version (integer, default is 0)
# - timestamp (integer seconds since epoch)
# - name (not encoded; assumed to be ~alphanumeric)
# - value (base64-encoded)
# - signature (hex-encoded; no length prefix)
def format_field(s):
return utf8("%d:" % len(s)) + utf8(s)
to_sign = b"|".join([
b"",
format_field(str(key_version or 0)),
format_field(timestamp),
format_field(name),
format_field(value),
b'']) if isinstance(secret, dict):
assert key_version is not None, 'Key version must be set when sign key dict is used'
assert version >= 2, 'Version must be at least 2 for key version support'
secret = secret[key_version] signature = _create_signature_v2(secret, to_sign)
return to_sign + signature
else:
raise ValueError("Unsupported version %d" % version)

全局create_signed_value

def _create_signature_v1(secret, *parts):
hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
for part in parts:
hash.update(utf8(part))
return utf8(hash.hexdigest()) def _create_signature_v2(secret, s):
hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
hash.update(utf8(s))
return utf8(hash.hexdigest())

加密V1和V2

这样做比较安全,当黑客如果知道你的加密过程伪造cookie,Tornado会根据它的cookie先去做签名,如果签名跟服务器上的一致那么就OK。但是这里有一个重要的因素是

secret = self.application.settings["cookie_secret"]  这个值是定在服务器这边的 很难获取到。如果这值知道了那么久没有任何办法了。

反解过程可以看self.get_secure_cookie() 过程都在这里面了。

Tornado(一)的更多相关文章

  1. Python(九)Tornado web 框架

    一.简介 Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过 ...

  2. 使用tornado,我们可以做什么?

    以下介绍都是建立在python2.x的基础上面,tornado使用任意版本皆可. 如果我们需要对外提供一个http server(web api)/websocket server时,我们都可以使用t ...

  3. tornado session

    [转]tornado入门 - session cookie 和session 的区别: 1.cookie数据存放在客户的浏览器上,session数据放在服务器上. 2.cookie不是很安全,别人可以 ...

  4. tornado template

    若果使用Tornado进行web开发可能会用到模板功能,页面继承,嵌套... 多页应用模板的处理多半依赖后端(SPA就可以动态加载局部视图),就算是RESTfull的API设计,也不妨碍同时提供部分模 ...

  5. tornado上手

    http://www.tornadoweb.org/en/stable/ http://www.cnblogs.com/fanweibin/p/5418697.html import tornado. ...

  6. tornado+sqlalchemy+celery,数据库连接消耗在哪里

    随着公司业务的发展,网站的日活数也逐渐增多,以前只需要考虑将所需要的功能实现就行了,当日活越来越大的时候,就需要考虑对服务器的资源使用消耗情况有一个清楚的认知.     最近老是发现数据库的连接数如果 ...

  7. centos 6.7 搭建tornado + nginx + supervisor的方法(已经实践)

    首先,本来不想写这篇博客了,但是我测试了很多网上的例子包括简书的,全不行,我总结原因是自己太笨,搞了俩个晚上,后来决定,自己还是写一篇记录下来,保证自己以后使用 环境: centos6.7 64 py ...

  8. tornado中将cookie值设置为json字符串

    不熟悉,找了很久,能FQ的话, https://groups.google.com/forum/#!topic/python-tornado/9Y--NgwjP_w 2楼有解释. tornado.es ...

  9. tornado 异步调用系统命令和非阻塞线程池

    项目中异步调用 ping 和 nmap 实现对目标 ip 和所在网关的探测 Subprocess.STREAM 不用担心进程返回数据过大造成的死锁, Subprocess.PIPE 会有这个问题. i ...

  10. 离线安装 Python 2.7, paramiko 和 tornado

    无非就是离线安装, 步骤比较繁琐, 记录一下. 需求很简单, 一个离线安装的 Python, 能跑 tornado 和 paramiko 1. 离线安装 Python 2.7 .tgz cd Pyth ...

随机推荐

  1. CF821 A. Okabe and Future Gadget Laboratory 水

    Link 题意:询问n X n中非1数是否能够由同行同列中分别取两个数做和得到. 思路:水题. /** @Date : 2017-07-03 16:23:18 * @FileName: A.cpp * ...

  2. ZOJ 3787 Access System 水

    LINK:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3787 思路:结构体 时间转化为秒记录下最小并且排序就好了 /** ...

  3. NYOJ 129 树的判定 (并查集)

    题目链接 描述 A tree is a well-known data structure that is either empty (null, void, nothing) or is a set ...

  4. 2017 WebStorm 激活码 更新 Pycharm同样可用

    [有效时间到2017 年 11月 23日] BIG3CLIK6F-eyJsaWNlbnNlSWQiOiJCSUczQ0xJSzZGIiwibGljZW5zZWVOYW1lIjoibGFuIHl1Iiw ...

  5. 新一代的USB 3.0传输规格

    通用序列总线(USB) 从1996问世以来,一统个人电脑外部连接界面,且延伸至各式消费性产品,早已成为现代人生活的一部分.2000年发表的USB 2.0 High-speed规格,提供了480Mbps ...

  6. nginx与PHP的关系和交互方式【转】

    nginx与PHP的关系. 对比, apache和PHP的关系, 将PHP安装成apache的一个功能模块, 导致的结果, 对外只有一个apache程序, PHP并不独立出现, 仅仅是apache的模 ...

  7. 不老的神器:安全扫描器Nmap渗透使用指南【转】

    介绍 nmap是用来探测计算机网络上的主机和服务的一种安全扫描器.为了绘制网络拓扑图Nmap的发送特制的数据包到目标主机然后对返回数据包进行分析.Nmap是一款枚举和测试网络的强大工具. 特点 主机探 ...

  8. HDU 6205 2017沈阳网络赛 思维题

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6205 题意:给你n堆牌,原本每一堆的所有牌(a[i]张)默认向下,每次从第一堆开始,将固定个数的牌(b ...

  9. javascript 之数据类型--01

    写在前面 国庆整理资料时,发现刚开始入门前端时学习JS 的资料,打算以一个基础入门博客记录下来,有不写不对的多多指教: 先推荐些书籍给需要的童鞋 <JavaScript 高级程序设计.pdf&g ...

  10. word2vec参数

    架构:skip-gram(慢.对罕见字有利)vs CBOW(快) ·         训练算法:分层softmax(对罕见字有利)vs 负采样(对常见词和低纬向量有利) 负例采样准确率提高,速度会慢, ...