预备知识
tornado框架session要自己写
cookie存储在客户端浏览器上,session数据放在服务器上
session依赖cookie
扩展tornado,返回请求前自定义session
面向对象的知识,obj['xxx']==>_getitems__,obj['xxx']=xxx ==> __setitems__, del obj['xxx'] ==> __delitems__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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']
session实现机制
tornado框架中,通过application的url映射到MainHandler类处理请求,根据请求的方式,通过反射执行对应的get或post等方法
tornado源码中有很多扩展点(钩子)供开发者使用,如在执行get或post方法之前先执行initialiaze方法,即使没有调用也执行
扩展initialiaze,用户请求进来,读一下用户的cookie,如果有的话,能够拿到,没有可以创建一个
为避免重复在每个映射的类里都写一遍initialiaze,可以在他们的共同的基类BaseHandler(自定义的类)里写initialiaze方法
其实这些类应该继承tornado.web.RequestHandler类,让BaseHandler继承这个类,其他类也就继承了
initialiaze中定义了self.my_session字段,它是一个session类的对象,init中获取或创建session
用户登录,通过__setitem__设置session中的内容,session保存在服务器中
session_container是用来保存session的字典
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
=
=
'123'
:
self
.my_session[
'c_user'
]
=
'wupeiqi'
self
.my_session[
'c_card'
]
=
'12312312309823012'
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可以存在其它地方,如redis,数据库,mongo等等
当session规模达到很大数量级,就采用多台分布式存储,经过一致性hash算法,分配到对应的机器上
根据cookie字符串转换成一串数字,每台机器分管的数字段区域不同,权重大的管的大
一致性哈希
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/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
"""
一致性哈希
tornado框架session要自己写
cookie存储在客户端浏览器上,session数据放在服务器上
session依赖cookie
扩展tornado,返回请求前自定义session
面向对象的知识,obj['xxx']==>_getitems__,obj['xxx']=xxx ==> __setitems__, del obj['xxx'] ==> __delitems__
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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'] |
tornado框架中,通过application的url映射到MainHandler类处理请求,根据请求的方式,通过反射执行对应的get或post等方法
tornado源码中有很多扩展点(钩子)供开发者使用,如在执行get或post方法之前先执行initialiaze方法,即使没有调用也执行
扩展initialiaze,用户请求进来,读一下用户的cookie,如果有的话,能够拿到,没有可以创建一个
为避免重复在每个映射的类里都写一遍initialiaze,可以在他们的共同的基类BaseHandler(自定义的类)里写initialiaze方法
其实这些类应该继承tornado.web.RequestHandler类,让BaseHandler继承这个类,其他类也就继承了
initialiaze中定义了self.my_session字段,它是一个session类的对象,init中获取或创建session
用户登录,通过__setitem__设置session中的内容,session保存在服务器中
session_container是用来保存session的字典
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
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 = = '123' :
self .my_session[ 'c_user' ] = 'wupeiqi'
self .my_session[ 'c_card' ] = '12312312309823012'
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可以存在其它地方,如redis,数据库,mongo等等
当session规模达到很大数量级,就采用多台分布式存储,经过一致性hash算法,分配到对应的机器上
根据cookie字符串转换成一串数字,每台机器分管的数字段区域不同,权重大的管的大
一致性哈希
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/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
"""
一致性哈希
session可以存在其它地方,如redis,数据库,mongo等等
当session规模达到很大数量级,就采用多台分布式存储,经过一致性hash算法,分配到对应的机器上
根据cookie字符串转换成一串数字,每台机器分管的数字段区域不同,权重大的管的大
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
#!/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 """ 一致性哈希 |
session
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
29
30
31
32
33
34
35
36
37
38
|
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)
|