Tornado 的性能是相当优异的,因为它试图解决一个被称之为“C10k”问题,就是处理大于或等于一万的并发。一万呀,这可是不小的量

条件:处理器为 AMD Opteron, 主频 2.4GHz, 4 核

安装 Tornado

pip install tornado

异步非阻塞示例

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web
from tornado import httpclient
from tornado.web import asynchronous
from tornado import gen import uimodules as md
import uimethods as mt class MainHandler(tornado.web.RequestHandler):
@asynchronous
@gen.coroutine
def get(self):
print 'start get '
http = httpclient.AsyncHTTPClient()
http.fetch("http://127.0.0.1:8008/post/", self.callback)
self.write('end') def callback(self, response):
print response.body settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'ui_methods': mt,
'ui_modules': md,
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8009)
tornado.ioloop.IOLoop.instance().start() 异步非阻塞示例 异步非阻塞示例

Tornado中原生支持二级域名的路由,如:

一、模板引擎

在模板中默认提供了一些函数、字段、类以供模板使用:

escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模组
handler: 当前的 RequestHandler 对象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名 其他方法 1、母版
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>老男孩</title>
<link href="{{static_url("css/common.css")}}" rel="stylesheet" />
{% block CSS %}{% end %}
</head>
<body> <div class="pg-header"> </div> {% block RenderBody %}{% end %} <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> {% block JavaScript %}{% end %}
</body>
</html>

layout.html

{% extends 'layout.html'%}
{% block CSS %}
<link href="{{static_url("css/index.css")}}" rel="stylesheet" />
{% end %} {% block RenderBody %}
<h1>Index</h1> <ul>
{% for item in li %}
<li>{{item}}</li>
{% end %}
</ul> {% end %} {% block JavaScript %} {% end %}

index.html

2、导入

<div>
<ul>
<li>1024</li>
<li>42区</li>
</ul>
</div>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>老男孩</title>
<link href="{{static_url("css/common.css")}}" rel="stylesheet" />
</head>
<body> <div class="pg-header">
{% include 'header.html' %}
</div> <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> </body>
</html>

index

3、自定义UIMethod以UIModule

a. 定义

# uimethods.py

def tab(self):
return 'UIMethod'
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule
from tornado import escape class custom(UIModule): def render(self, *args, **kwargs):
return escape.xhtml_escape('<h1>wupeiqi</h1>')
#return escape.xhtml_escape('<h1>wupeiqi</h1>')

uimodules.py

b. 注册

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web
from tornado.escape import linkify
import uimodules as md
import uimethods as mt class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html') settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'ui_methods': mt,
'ui_modules': md,
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8009)
tornado.ioloop.IOLoop.instance().start()

c. 使用

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>hello</h1>
{% module custom(123) %}
{{ tab() }}
</body>

二、静态文件

对于静态文件,可以配置静态文件的目录和前段使用时的前缀,并且Tornaodo还支持静态文件缓存。

settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
}
<link href="{{static_url("commons.css")}}" rel="stylesheet" />

静态文件缓存的实现

def get_content_version(cls, abspath):
"""Returns a version string for the resource at the given path. This class method may be overridden by subclasses. The
default implementation is a hash of the file's contents. .. versionadded:: 3.1
"""
data = cls.get_content(abspath)
hasher = hashlib.md5()
if isinstance(data, bytes):
hasher.update(data)
else:
for chunk in data:
hasher.update(chunk)
return hasher.hexdigest()

三、cookie

Tornado中可以对cookie进行操作,并且还可以对cookie进行签名以放置伪造。

1、基本操作

class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_cookie("mycookie"):
self.set_cookie("mycookie", "myvalue")
self.write("Your cookie was not set yet!")
else:
self.write("Your cookie was set!")

2、加密cookie(签名)

Cookie 很容易被恶意的客户端伪造。你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能

class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_secure_cookie("mycookie"):
self.set_secure_cookie("mycookie", "myvalue")
self.write("Your cookie was not set yet!")
else:
self.write("Your cookie was set!") application = tornado.web.Application([
(r"/", MainHandler),
], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
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()) 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())))
value = base64.b64encode(utf8(value))
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) # 解密
def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
parts = utf8(value).split(b"|")
if len(parts) != 3:
return None
signature = _create_signature_v1(secret, name, parts[0], parts[1])
if not _time_independent_equals(parts[2], signature):
gen_log.warning("Invalid cookie signature %r", value)
return None
timestamp = int(parts[1])
if timestamp < clock() - max_age_days * 86400:
gen_log.warning("Expired cookie %r", value)
return None
if timestamp > clock() + 31 * 86400:
# _cookie_signature does not hash a delimiter between the
# parts of the cookie, so an attacker could transfer trailing
# digits from the payload to the timestamp without altering the
# signature. For backwards compatibility, sanity-check timestamp
# here instead of modifying _cookie_signature.
gen_log.warning("Cookie timestamp in future; possible tampering %r",
value)
return None
if parts[1].startswith(b""):
gen_log.warning("Tampered cookie %r", value)
return None
try:
return base64.b64decode(parts[0])
except Exception:
return None def _decode_fields_v2(value):
def _consume_field(s):
length, _, rest = s.partition(b':')
n = int(length)
field_value = rest[:n]
# In python 3, indexing bytes returns small integers; we must
# use a slice to get a byte string as in python 2.
if rest[n:n + 1] != b'|':
raise ValueError("malformed v2 signed value field")
rest = rest[n + 1:]
return field_value, rest rest = value[2:] # remove version number
key_version, rest = _consume_field(rest)
timestamp, rest = _consume_field(rest)
name_field, rest = _consume_field(rest)
value_field, passed_sig = _consume_field(rest)
return int(key_version), timestamp, name_field, value_field, passed_sig def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
try:
key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
except ValueError:
return None
signed_string = value[:-len(passed_sig)] if isinstance(secret, dict):
try:
secret = secret[key_version]
except KeyError:
return None expected_sig = _create_signature_v2(secret, signed_string)
if not _time_independent_equals(passed_sig, expected_sig):
return None
if name_field != utf8(name):
return None
timestamp = int(timestamp)
if timestamp < clock() - max_age_days * 86400:
# The signature has expired.
return None
try:
return base64.b64decode(value_field)
except Exception:
return None def get_signature_key_version(value):
value = utf8(value)
version = _get_version(value)
if version < 2:
return None
try:
key_version, _, _, _, _ = _decode_fields_v2(value)
except ValueError:
return None return key_version

内部算法

签名Cookie的本质是:

写cookie过程:

  • 将值进行base64加密
  • 对除值以外的内容进行签名,哈希算法(无法逆向解析)
  • 拼接 签名 + 加密值

读cookie过程:

  • 读取 签名 + 加密值
  • 对签名进行验证
  • base64解密,获取值内容

注:许多API验证机制和安全cookie的实现机制相同。

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self):
login_user = self.get_secure_cookie("login_user", None)
if login_user:
self.write(login_user)
else:
self.redirect('/login') class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.current_user() self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('name')
password = self.get_argument('pwd')
if username == 'wupeiqi' and password == '':
self.set_secure_cookie('login_user', '武沛齐')
self.redirect('/')
else:
self.render('login.html', **{'status': '用户名或密码错误'}) settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

基于Cookie实现用户验证-Demo

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web class BaseHandler(tornado.web.RequestHandler): def get_current_user(self):
return self.get_secure_cookie("login_user") class MainHandler(BaseHandler): @tornado.web.authenticated
def get(self):
login_user = self.current_user
self.write(login_user) class LoginHandler(tornado.web.RequestHandler):
def get(self):
self.current_user() self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('name')
password = self.get_argument('pwd')
if username == 'wupeiqi' and password == '':
self.set_secure_cookie('login_user', '武沛齐')
self.redirect('/')
else:
self.render('login.html', **{'status': '用户名或密码错误'}) settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
'login_url': '/login'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

基于签名Cookie实现用户验证-Demo

3、JavaScript操作Cookie

由于Cookie保存在浏览器端,所以在浏览器端也可以使用JavaScript来操作Cookie。

/*
设置cookie,指定秒数过期
*/
function setCookie(name,value,expires){
var temp = [];
var current_date = new Date();
current_date.setSeconds(current_date.getSeconds() + 5);
document.cookie = name + "= "+ value +";expires=" + current_date.toUTCString();
}

对于参数:

  • domain   指定域名下的cookie
  • path       域名下指定url中的cookie
  • secure    https使用

注:jQuery中也有指定的插件 jQuery Cookie 专门用于操作cookie   http://plugins.jquery.com/cookie/

四、CSRF

Tornado中的夸张请求伪造和Django中的相似

settings = {
"xsrf_cookies": True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
<form action="/new_message" method="post">
{{ xsrf_form_html() }}
<input type="text" name="message"/>
<input type="submit" value="Post"/>
</form>

普通表单

function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
} jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
callback(eval("(" + response + ")"));
}});
};

使用 - AJAX

注:Ajax使用时,本质上就是去获取本地的cookie,携带cookie再来发送请求

五、上传文件

1、Form表单上传

class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
file_metas = self.request.files["fff"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body'])
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>上传文件</title>
</head>
<body>
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<input name="fff" id="my_file" type="file" />
<input type="submit" value="提交" />
</form>
</body>
</html>

HTML

2、AJAX上传

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = document.getElementById("img").files[0]; var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); var xhr = new XMLHttpRequest();
xhr.open("post", '/index', true);
xhr.send(form);
}
</script>
</body>
</html>

HTML - XMLHttpRequest

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = $("#img")[0].files[0];
var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj); $.ajax({
type:'POST',
url: '/index',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success: function(arg){
console.log(arg);
}
})
}
</script>
</body>
</html>

HTML - jQuery

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<div id="main">
<input name="fff" id="my_file" type="file" />
<input type="button" name="action" value="Upload" onclick="redirect()"/>
<iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe>
</div>
</form> <script>
function redirect(){
document.getElementById('my_iframe').onload = Testt;
document.getElementById('my_form').target = 'my_iframe';
document.getElementById('my_form').submit(); } function Testt(ths){
var t = $("#my_iframe").contents().find("body").text();
console.log(t);
}
</script>
</body>
</html>

HTML - iframe

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web class MainHandler(tornado.web.RequestHandler):
def get(self): self.render('index.html') def post(self, *args, **kwargs):
file_metas = self.request.files["fff"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body']) settings = {
'template_path': 'template',
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()

Python

<script type="text/javascript">

    $(document).ready(function () {

        $("#formsubmit").click(function () {

            var iframe = $('<iframe name="postiframe" id="postiframe" style="display: none"></iframe>');

            $("body").append(iframe);

            var form = $('#theuploadform');
form.attr("action", "/upload.aspx");
form.attr("method", "post"); form.attr("encoding", "multipart/form-data");
form.attr("enctype", "multipart/form-data"); form.attr("target", "postiframe");
form.attr("file", $('#userfile').val());
form.submit(); $("#postiframe").load(function () {
iframeContents = this.contentWindow.document.body.innerHTML;
$("#textarea").html(iframeContents);
}); return false; }); }); </script> <form id="theuploadform">
<input id="userfile" name="userfile" size="50" type="file" />
<input id="formsubmit" type="submit" value="Send File" />
</form> <div id="textarea">
</div>

扩展:基于iframe实现Ajax上传示例

六、验证码

验证码原理在于后台自动创建一张带有随机内容的图片,然后将内容通过img标签输出到页面。

安装图像处理模块:pip3 install pillow

示例截图:

class CheckCodeHandler(BaseRequestHandler):
def get(self, *args, **kwargs):
stream = io.BytesIO()
img, code = check_code.create_validate_code()
img.save(stream, "png")
self.session["CheckCode"] = code
self.write(stream.getvalue())

自定义Web组件

一、Session

1、面向对象基础

面向对象中通过索引的方式访问对象,需要内部实现 __getitem__ 、__delitem__、__setitem__方法

#!/usr/bin/env python
# -*- coding:utf-8 -*- class Foo(object): def __getitem__(self, key):
print '__getitem__',key def __setitem__(self, key, value):
print '__setitem__',key,value def __delitem__(self, key):
print '__delitem__',key obj = Foo()
result = obj['k1']
#obj['k2'] = 'wupeiqi'
#del obj['k1']

2、Tornado扩展

Tornado框架中,默认执行Handler的get/post等方法之前默认会执行 initialize方法,所以可以通过自定义的方式使得所有请求在处理前执行操作...

class BaseHandler(tornado.web.RequestHandler):

    def initialize(self):
self.xxoo = "wupeiqi" class MainHandler(BaseHandler): def get(self):
print(self.xxoo)
self.write('index') class IndexHandler(BaseHandler): def get(self):
print(self.xxoo)
self.write('index')

3、session

session其实就是定义在服务器端用于保存用户会话的容器,其必须依赖cookie才能实现。

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time session_container = {} create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest() class Session(object): session_id = "__sessionId__" def __init__(self, request):
session_value = request.get_cookie(Session.session_id)
if not session_value:
self._id = create_session_id()
else:
self._id = session_value
request.set_cookie(Session.session_id, self._id) def __getitem__(self, key):
return session_container[self._id][key] def __setitem__(self, key, value):
if session_container.has_key(self._id):
session_container[self._id][key] = value
else:
session_container[self._id] = {key: value} def __delitem__(self, key):
del session_container[self._id][key] class BaseHandler(tornado.web.RequestHandler): def initialize(self):
# my_session['k1']访问 __getitem__ 方法
self.my_session = Session(self) class MainHandler(BaseHandler): def get(self):
print self.my_session['c_user']
print self.my_session['c_card']
self.write('index') class LoginHandler(BaseHandler): def get(self):
self.render('login.html', **{'status': ''}) def post(self, *args, **kwargs): username = self.get_argument('name')
password = self.get_argument('pwd')
if username == 'wupeiqi' and password == '': self.my_session['c_user'] = 'wupeiqi'
self.my_session['c_card'] = '' self.redirect('/index')
else:
self.render('login.html', **{'status': '用户名或密码错误'}) settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
'login_url': '/login'
} application = tornado.web.Application([
(r"/index", MainHandler),
(r"/login", LoginHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start() 自定义Session 自定义Session

4、分布式Session

#!/usr/bin/env python
#coding:utf-8 import sys
import math
from bisect import bisect if sys.version_info >= (2, 5):
import hashlib
md5_constructor = hashlib.md5
else:
import md5
md5_constructor = md5.new class HashRing(object):
"""一致性哈希""" def __init__(self,nodes):
'''初始化
nodes : 初始化的节点,其中包含节点已经节点对应的权重
默认每一个节点有32个虚拟节点
对于权重,通过多创建虚拟节点来实现
如:nodes = [
{'host':'127.0.0.1:8000','weight':1},
{'host':'127.0.0.1:8001','weight':2},
{'host':'127.0.0.1:8002','weight':1},
]
''' self.ring = dict()
self._sorted_keys = [] self.total_weight = 0 self.__generate_circle(nodes) def __generate_circle(self,nodes):
for node_info in nodes:
self.total_weight += node_info.get('weight',1) for node_info in nodes:
weight = node_info.get('weight',1)
node = node_info.get('host',None) virtual_node_count = math.floor((32*len(nodes)*weight) / self.total_weight)
for i in xrange(0,int(virtual_node_count)):
key = self.gen_key_thirty_two( '%s-%s' % (node, i) )
if self._sorted_keys.__contains__(key):
raise Exception('该节点已经存在.')
self.ring[key] = node
self._sorted_keys.append(key) def add_node(self,node):
''' 新建节点
node : 要添加的节点,格式为:{'host':'127.0.0.1:8002','weight':1},其中第一个元素表示节点,第二个元素表示该节点的权重。
'''
node = node.get('host',None)
if not node:
raise Exception('节点的地址不能为空.') weight = node.get('weight',1) self.total_weight += weight
nodes_count = len(self._sorted_keys) + 1 virtual_node_count = math.floor((32 * nodes_count * weight) / self.total_weight)
for i in xrange(0,int(virtual_node_count)):
key = self.gen_key_thirty_two( '%s-%s' % (node, i) )
if self._sorted_keys.__contains__(key):
raise Exception('该节点已经存在.')
self.ring[key] = node
self._sorted_keys.append(key) def remove_node(self,node):
''' 移除节点
node : 要移除的节点 '127.0.0.1:8000'
'''
for key,value in self.ring.items():
if value == node:
del self.ring[key]
self._sorted_keys.remove(key) def get_node(self,string_key):
'''获取 string_key 所在的节点'''
pos = self.get_node_pos(string_key)
if pos is None:
return None
return self.ring[ self._sorted_keys[pos]].split(':') def get_node_pos(self,string_key):
'''获取 string_key 所在的节点的索引'''
if not self.ring:
return None key = self.gen_key_thirty_two(string_key)
nodes = self._sorted_keys
pos = bisect(nodes, key)
return pos def gen_key_thirty_two(self, key): m = md5_constructor()
m.update(key)
return long(m.hexdigest(), 16) def gen_key_sixteen(self,key): b_key = self.__hash_digest(key)
return self.__hash_val(b_key, lambda x: x) def __hash_val(self, b_key, entry_fn):
return (( b_key[entry_fn(3)] << 24)|(b_key[entry_fn(2)] << 16)|(b_key[entry_fn(1)] << 8)| b_key[entry_fn(0)] ) def __hash_digest(self, key):
m = md5_constructor()
m.update(key)
return map(ord, m.digest()) """
nodes = [
{'host':'127.0.0.1:8000','weight':1},
{'host':'127.0.0.1:8001','weight':2},
{'host':'127.0.0.1:8002','weight':1},
] ring = HashRing(nodes)
result = ring.get_node('98708798709870987098709879087')
print result """

一致性哈西

from hashlib import sha1
import os, time create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest() class Session(object): session_id = "__sessionId__" def __init__(self, request):
session_value = request.get_cookie(Session.session_id)
if not session_value:
self._id = create_session_id()
else:
self._id = session_value
request.set_cookie(Session.session_id, self._id) def __getitem__(self, key):
# 根据 self._id ,在一致性哈西中找到其对应的服务器IP
# 找到相对应的redis服务器,如: r = redis.StrictRedis(host='localhost', port=6379, db=0)
# 使用python redis api 链接
# 获取数据,即:
# return self._redis.hget(self._id, name) def __setitem__(self, key, value):
# 根据 self._id ,在一致性哈西中找到其对应的服务器IP
# 使用python redis api 链接
# 设置session
# self._redis.hset(self._id, name, value) def __delitem__(self, key):
# 根据 self._id 找到相对应的redis服务器
# 使用python redis api 链接
# 删除,即:
return self._redis.hdel(self._id, name)

session

二、表单验证

在Web程序中往往包含大量的表单验证的工作,如:判断输入是否为空,是否符合规则。

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<link href="{{static_url("commons.css")}}" rel="stylesheet" />
</head>
<body>
<h1>hello</h1>
<form action="/index" method="post"> <p>hostname: <input type="text" name="host" /> </p>
<p>ip: <input type="text" name="ip" /> </p>
<p>port: <input type="text" name="port" /> </p>
<p>phone: <input type="text" name="phone" /> </p>
<input type="submit" />
</form>
</body>
</html>

HTML

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web
from hashlib import sha1
import os, time
import re class MainForm(object):
def __init__(self):
self.host = "(.*)"
self.ip = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$"
self.port = '(\d+)'
self.phone = '^1[3|4|5|8][0-9]\d{8}$' def check_valid(self, request):
form_dict = self.__dict__
for key, regular in form_dict.items():
post_value = request.get_argument(key)
# 让提交的数据 和 定义的正则表达式进行匹配
ret = re.match(regular, post_value)
print key,ret,post_value class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def post(self, *args, **kwargs):
obj = MainForm()
result = obj.check_valid(self)
self.write('ok') settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
'login_url': '/login'
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Python

由于验证规则可以代码重用,所以可以如此定义:

#!/usr/bin/env python
# -*- coding:utf-8 -*- import tornado.ioloop
import tornado.web
import re class Field(object): def __init__(self, error_msg_dict, required):
self.id_valid = False
self.value = None
self.error = None
self.name = None
self.error_msg = error_msg_dict
self.required = required def match(self, name, value):
self.name = name if not self.required:
self.id_valid = True
self.value = value
else:
if not value:
if self.error_msg.get('required', None):
self.error = self.error_msg['required']
else:
self.error = "%s is required" % name
else:
ret = re.match(self.REGULAR, value)
if ret:
self.id_valid = True
self.value = ret.group()
else:
if self.error_msg.get('valid', None):
self.error = self.error_msg['valid']
else:
self.error = "%s is invalid" % name class IPField(Field):
REGULAR = "^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$" def __init__(self, error_msg_dict=None, required=True): error_msg = {} # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
if error_msg_dict:
error_msg.update(error_msg_dict) super(IPField, self).__init__(error_msg_dict=error_msg, required=required) class IntegerField(Field):
REGULAR = "^\d+$" def __init__(self, error_msg_dict=None, required=True):
error_msg = {'required': '数字不能为空', 'valid': '数字格式错误'}
if error_msg_dict:
error_msg.update(error_msg_dict) super(IntegerField, self).__init__(error_msg_dict=error_msg, required=required) class CheckBoxField(Field): def __init__(self, error_msg_dict=None, required=True):
error_msg = {} # {'required': 'IP不能为空', 'valid': 'IP格式错误'}
if error_msg_dict:
error_msg.update(error_msg_dict) super(CheckBoxField, self).__init__(error_msg_dict=error_msg, required=required) def match(self, name, value):
self.name = name if not self.required:
self.id_valid = True
self.value = value
else:
if not value:
if self.error_msg.get('required', None):
self.error = self.error_msg['required']
else:
self.error = "%s is required" % name
else:
if isinstance(name, list):
self.id_valid = True
self.value = value
else:
if self.error_msg.get('valid', None):
self.error = self.error_msg['valid']
else:
self.error = "%s is invalid" % name class FileField(Field):
REGULAR = "^(\w+\.pdf)|(\w+\.mp3)|(\w+\.py)$" def __init__(self, error_msg_dict=None, required=True):
error_msg = {} # {'required': '数字不能为空', 'valid': '数字格式错误'}
if error_msg_dict:
error_msg.update(error_msg_dict) super(FileField, self).__init__(error_msg_dict=error_msg, required=required) def match(self, name, value):
self.name = name
self.value = []
if not self.required:
self.id_valid = True
self.value = value
else:
if not value:
if self.error_msg.get('required', None):
self.error = self.error_msg['required']
else:
self.error = "%s is required" % name
else:
m = re.compile(self.REGULAR)
if isinstance(value, list):
for file_name in value:
r = m.match(file_name)
if r:
self.value.append(r.group())
self.id_valid = True
else:
self.id_valid = False
if self.error_msg.get('valid', None):
self.error = self.error_msg['valid']
else:
self.error = "%s is invalid" % name
break
else:
if self.error_msg.get('valid', None):
self.error = self.error_msg['valid']
else:
self.error = "%s is invalid" % name def save(self, request, upload_path=""): file_metas = request.files[self.name]
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body']) class Form(object): def __init__(self):
self.value_dict = {}
self.error_dict = {}
self.valid_status = True def validate(self, request, depth=10, pre_key=""): self.initialize()
self.__valid(self, request, depth, pre_key) def initialize(self):
pass def __valid(self, form_obj, request, depth, pre_key):
"""
验证用户表单请求的数据
:param form_obj: Form对象(Form派生类的对象)
:param request: Http请求上下文(用于从请求中获取用户提交的值)
:param depth: 对Form内容的深度的支持
:param pre_key: Html中name属性值的前缀(多层Form时,内部递归时设置,无需理会)
:return: 是否验证通过,True:验证成功;False:验证失败
""" depth -= 1
if depth < 0:
return None
form_field_dict = form_obj.__dict__
for key, field_obj in form_field_dict.items():
print key,field_obj
if isinstance(field_obj, Form) or isinstance(field_obj, Field):
if isinstance(field_obj, Form):
# 获取以key开头的所有的值,以参数的形式传至
self.__valid(field_obj, request, depth, key)
continue
if pre_key:
key = "%s.%s" % (pre_key, key) if isinstance(field_obj, CheckBoxField):
post_value = request.get_arguments(key, None)
elif isinstance(field_obj, FileField):
post_value = []
file_list = request.request.files.get(key, None)
for file_item in file_list:
post_value.append(file_item['filename'])
else:
post_value = request.get_argument(key, None) print post_value
# 让提交的数据 和 定义的正则表达式进行匹配
field_obj.match(key, post_value)
if field_obj.id_valid:
self.value_dict[key] = field_obj.value
else:
self.error_dict[key] = field_obj.error
self.valid_status = False class ListForm(object):
def __init__(self, form_type):
self.form_type = form_type
self.valid_status = True
self.value_dict = {}
self.error_dict = {} def validate(self, request):
name_list = request.request.arguments.keys() + request.request.files.keys()
index = 0
flag = False
while True:
pre_key = "[%d]" % index
for name in name_list:
if name.startswith(pre_key):
flag = True
break
if flag:
form_obj = self.form_type()
form_obj.validate(request, depth=10, pre_key="[%d]" % index)
if form_obj.valid_status:
self.value_dict[index] = form_obj.value_dict
else:
self.error_dict[index] = form_obj.error_dict
self.valid_status = False
else:
break index += 1
flag = False class MainForm(Form): def __init__(self):
# self.ip = IPField(required=True)
# self.port = IntegerField(required=True)
# self.new_ip = IPField(required=True)
# self.second = SecondForm()
self.fff = FileField(required=True)
super(MainForm, self).__init__() #
# class SecondForm(Form):
#
# def __init__(self):
# self.ip = IPField(required=True)
# self.new_ip = IPField(required=True)
#
# super(SecondForm, self).__init__() class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def post(self, *args, **kwargs):
# for i in dir(self.request):
# print i
# print self.request.arguments
# print self.request.files
# print self.request.query
# name_list = self.request.arguments.keys() + self.request.files.keys()
# print name_list # list_form = ListForm(MainForm)
# list_form.validate(self)
#
# print list_form.valid_status
# print list_form.value_dict
# print list_form.error_dict # obj = MainForm()
# obj.validate(self)
#
# print "验证结果:", obj.valid_status
# print "符合验证结果:", obj.value_dict
# print "错误信息:"
# for key, item in obj.error_dict.items():
# print key,item
# print self.get_arguments('favor'),type(self.get_arguments('favor'))
# print self.get_argument('favor'),type(self.get_argument('favor'))
# print type(self.get_argument('fff')),self.get_argument('fff')
# print self.request.files
# obj = MainForm()
# obj.validate(self)
# print obj.valid_status
# print obj.value_dict
# print obj.error_dict
# print self.request,type(self.request)
# obj.fff.save(self.request)
# from tornado.httputil import HTTPServerRequest
# name_list = self.request.arguments.keys() + self.request.files.keys()
# print name_list
# print self.request.files,type(self.request.files)
# print len(self.request.files.get('fff')) # obj = MainForm()
# obj.validate(self)
# print obj.valid_status
# print obj.value_dict
# print obj.error_dict
# obj.fff.save(self.request)
self.write('ok') settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': '/static/',
'cookie_secret': 'aiuasdhflashjdfoiuashdfiuh',
'login_url': '/login'
} application = tornado.web.Application([
(r"/index", MainHandler),
], **settings) if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

Tornado介绍及自定义组件的更多相关文章

  1. Android开发之自定义组件和接口回调

    说到自定义控件不得不提的就是接口回调,在Android开发中接口回调用的还是蛮多的.在这篇博客开始的时候呢,我想聊一下iOS的自定义控件.在iOS中自定义控件的思路是继承自UIView, 在UIVie ...

  2. Android开发——构建自定义组件

    Android中,你的应用程序程序与View类组件有着一种固定的联系,例如按钮(Button). 文本框(TextView), 可编辑文本框(EditText), 列表框(ListView), 复选框 ...

  3. 【Android 应用开发】 自定义组件 宽高适配方法, 手势监听器操作组件, 回调接口维护策略, 绘制方法分析 -- 基于 WheelView 组件分析自定义组件

    博客地址 : http://blog.csdn.net/shulianghan/article/details/41520569 代码下载 : -- GitHub : https://github.c ...

  4. 第三部分:Android 应用程序接口指南---第二节:UI---第十二章 自定义组件

    第12章 自定义组件 Android平台提供了一套完备的.功能强大的组件化模型用于搭建用户界面,这套组件化模型以View和 ViewGroup这两个基础布局类为基础.平台本身已预先实现了多种用于构建界 ...

  5. {Django基础九之中间件} 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证

    Django基础九之中间件 本节目录 一 前戏 二 中间件介绍 三 自定义中间件 四 中间件的执行流程 五 中间件版登陆认证 六 xxx 七 xxx 八 xxx 一 前戏 我们在前面的课程中已经学会了 ...

  6. 微信小程序 发现之旅(二)—— 自定义组件

    组件化的项目开发中,组件应当划分为三个层次:组件.模块.页面 微信小程序已经为开发者封装好了基础组件,页面文件(pages)也有了详细的规定 而模块就需要自行开发,并且要和页面文件区分开,这就涉及到自 ...

  7. Vuejs中slot实现自定义组件header、footer等

    Vuejs中slot实现自定义组件header.footer等 vue中的slot主要负责内容分发,之前有介绍过slot的内容,具体链接:http://www.cnblogs.com/vipzhou/ ...

  8. VUE 自定义组件之间的相互通信

    一.自定义组件 1.全局自定义组件 我们在var vm = new Vue({});的上面并列写上Vue.component('自定义组件名',{组件对象});来完成全局自定义组件的声明.示例代码如下 ...

  9. Android自定义组件系列【5】——进阶实践(2)

    上一篇<Android自定义组件系列[5]--进阶实践(1)>中对任老师的<可下拉的PinnedHeaderExpandableListView的实现>前一部分进行了实现,这一 ...

随机推荐

  1. xshell无法登录阿里云服务器

    1. 现象 a. 使用xshell无法登录服务器 b. 查看sshd服务 c. 不同公网ip的电脑正常登陆 2. 解决方案 a. 不同公网ip可以登录,断定是ip被黑名单,联系阿里云服务,把公网ip加 ...

  2. 可重复执行的SQL Script

    问题 在工作中偶尔会遇到这样的问题:SQL script重复执行时会报错. 理想的状态下,SQL script跑一遍就够了,是不会重复执行的,但是实际情况往往很复杂. 比如Dev同学在开发时在A环境把 ...

  3. 如何直接在github上预览html网页效果

    http://justcoding.iteye.com/blog/2321552 2.vue 配合vue-resource调用接口,获取数据   https://www.cnblogs.com/fei ...

  4. opacity 与rgba区别

    rgba(r,g,b,a) rgba(r,g,b,a) r,g,b分别是颜色r g b的值(0-255),a表示透明度(0-1). opacity: value: opacity: value; va ...

  5. Spring与Akka的集成

    概述 近年来随着Spark的火热,Spark本身使用的开发语言Scala.用到的分布式内存文件系统Tachyon(现已更名为Alluxio)以及基于Actor并发编程模型的Akka都引起了大家的注意. ...

  6. java容器类3:set/HastSet/MapSet深入解读

    介绍 Set:集合,是一个不包含重复数据的集合.(A collection that contains no duplicate elements. ) set中最多包含一个null元素,否者包含了两 ...

  7. MSIL实用指南-局部变量的声明、保存和加载

    这一篇讲解方法内的局部变量是怎么声明.怎样保存.怎样加载的. 声明局部变量声明用ILGenerator的DeclareLocal方法,参数是局部变量的数据类型,得到一个局部变量对应的创建类LocalB ...

  8. Windows下python2.7安装64位mysqlclient

    在使用python调用mysql数据库的时候,需要引入对应的数据包 import MySQLdb 如果提示找不到对应的包,说明还需要安装对应的mysql数据库客户端操作的python包.在python ...

  9. Numpy库(个人学习笔记)

    一样,咱的计算机还是得先拥有Python,并且安装了Numpy库.有疑问的话可以看这里呦~~~~ 下面开讲: NumPy的主要对象是齐次多维数组.它是一个元素表(通常是数字),并且都是相同类型,由正整 ...

  10. 常见的if语句shell脚本

    常见的if语句shell脚本 author :headsen  chen  2017-10-17  15:00:07 1,cat if.sh 2, cat  if2.sh