tornado框架之cookie&session

发布时间 2023-12-08 15:17:42作者: 木屐呀

一、cookie

Tornado中可以对cookie进行操作,并且还可以对cookie进行签名以防止伪造

a、基本操作

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

b、签名

Cookie 很容易被恶意的客户端伪造。加入你想在 cookie 中保存当前登陆用户的 id 之类的信息,你需要对 cookie 作签名以防止伪造。Tornado 通过 set_secure_cookie 和 get_secure_cookie 方法直接支持了这种功能。 要使用这些方法,你需要在创建应用时提供一个密钥,名字为 cookie_secret。 你可以把它作为一个关键词参数传入应用的设置中:

 1 class MainHandler(tornado.web.RequestHandler):
 2     def get(self):
 3         if not self.get_secure_cookie("mycookie"):
 4             self.set_secure_cookie("mycookie", "myvalue")
 5             self.write("Your cookie was not set yet!")
 6         else:
 7             self.write("Your cookie was set!")
 8              
 9 application = tornado.web.Application([
10     (r"/", MainHandler),
11 ], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
 1 def _create_signature_v1(secret, *parts):
 2     hash = hmac.new(utf8(secret), digestmod=hashlib.sha1)
 3     for part in parts:
 4         hash.update(utf8(part))
 5     return utf8(hash.hexdigest())
 6 
 7 
 8 def _create_signature_v2(secret, s):
 9     hash = hmac.new(utf8(secret), digestmod=hashlib.sha256)
10     hash.update(utf8(s))
11     return utf8(hash.hexdigest())
内部算法
 1 def create_signed_value(secret, name, value, version=None, clock=None,
 2                         key_version=None):
 3     if version is None:
 4         version = DEFAULT_SIGNED_VALUE_VERSION
 5     if clock is None:
 6         clock = time.time
 7 
 8     timestamp = utf8(str(int(clock())))
 9     value = base64.b64encode(utf8(value))
10     if version == 1:
11         signature = _create_signature_v1(secret, name, value, timestamp)
12         value = b"|".join([value, timestamp, signature])
13         return value
14     elif version == 2:
15         # The v2 format consists of a version number and a series of
16         # length-prefixed fields "%d:%s", the last of which is a
17         # signature, all separated by pipes.  All numbers are in
18         # decimal format with no leading zeros.  The signature is an
19         # HMAC-SHA256 of the whole string up to that point, including
20         # the final pipe.
21         #
22         # The fields are:
23         # - format version (i.e. 2; no length prefix)
24         # - key version (integer, default is 0)
25         # - timestamp (integer seconds since epoch)
26         # - name (not encoded; assumed to be ~alphanumeric)
27         # - value (base64-encoded)
28         # - signature (hex-encoded; no length prefix)
29         def format_field(s):
30             return utf8("%d:" % len(s)) + utf8(s)
31         to_sign = b"|".join([
32             b"2",
33             format_field(str(key_version or 0)),
34             format_field(timestamp),
35             format_field(name),
36             format_field(value),
37             b''])
38 
39         if isinstance(secret, dict):
40             assert key_version is not None, 'Key version must be set when sign key dict is used'
41             assert version >= 2, 'Version must be at least 2 for key version support'
42             secret = secret[key_version]
43 
44         signature = _create_signature_v2(secret, to_sign)
45         return to_sign + signature
46     else:
47         raise ValueError("Unsupported version %d" % version)
内部算法-加密
 1 def _decode_signed_value_v1(secret, name, value, max_age_days, clock):
 2     parts = utf8(value).split(b"|")
 3     if len(parts) != 3:
 4         return None
 5     signature = _create_signature_v1(secret, name, parts[0], parts[1])
 6     if not _time_independent_equals(parts[2], signature):
 7         gen_log.warning("Invalid cookie signature %r", value)
 8         return None
 9     timestamp = int(parts[1])
10     if timestamp < clock() - max_age_days * 86400:
11         gen_log.warning("Expired cookie %r", value)
12         return None
13     if timestamp > clock() + 31 * 86400:
14         # _cookie_signature does not hash a delimiter between the
15         # parts of the cookie, so an attacker could transfer trailing
16         # digits from the payload to the timestamp without altering the
17         # signature.  For backwards compatibility, sanity-check timestamp
18         # here instead of modifying _cookie_signature.
19         gen_log.warning("Cookie timestamp in future; possible tampering %r",
20                         value)
21         return None
22     if parts[1].startswith(b"0"):
23         gen_log.warning("Tampered cookie %r", value)
24         return None
25     try:
26         return base64.b64decode(parts[0])
27     except Exception:
28         return None
29 
30 
31 def _decode_fields_v2(value):
32     def _consume_field(s):
33         length, _, rest = s.partition(b':')
34         n = int(length)
35         field_value = rest[:n]
36         # In python 3, indexing bytes returns small integers; we must
37         # use a slice to get a byte string as in python 2.
38         if rest[n:n + 1] != b'|':
39             raise ValueError("malformed v2 signed value field")
40         rest = rest[n + 1:]
41         return field_value, rest
42 
43     rest = value[2:]  # remove version number
44     key_version, rest = _consume_field(rest)
45     timestamp, rest = _consume_field(rest)
46     name_field, rest = _consume_field(rest)
47     value_field, passed_sig = _consume_field(rest)
48     return int(key_version), timestamp, name_field, value_field, passed_sig
49 
50 
51 def _decode_signed_value_v2(secret, name, value, max_age_days, clock):
52     try:
53         key_version, timestamp, name_field, value_field, passed_sig = _decode_fields_v2(value)
54     except ValueError:
55         return None
56     signed_string = value[:-len(passed_sig)]
57 
58     if isinstance(secret, dict):
59         try:
60             secret = secret[key_version]
61         except KeyError:
62             return None
63 
64     expected_sig = _create_signature_v2(secret, signed_string)
65     if not _time_independent_equals(passed_sig, expected_sig):
66         return None
67     if name_field != utf8(name):
68         return None
69     timestamp = int(timestamp)
70     if timestamp < clock() - max_age_days * 86400:
71         # The signature has expired.
72         return None
73     try:
74         return base64.b64decode(value_field)
75     except Exception:
76         return None
77 
78 
79 def get_signature_key_version(value):
80     value = utf8(value)
81     version = _get_version(value)
82     if version < 2:
83         return None
84     try:
85         key_version, _, _, _, _ = _decode_fields_v2(value)
86     except ValueError:
87         return None
88 
89     return key_version
内部算法-解密

签名Cookie的本质是:

 1 写cookie过程:
 2 
 3 将值进行base64加密
 4 对除值以外的内容进行签名,哈希算法(无法逆向解析)
 5 拼接 签名 + 加密值
 6 读cookie过程:
 7 
 8 读取 签名 + 加密值
 9 对签名进行验证
10 base64解密,获取值内容

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

1.Demo-基于cookie进行用户验证

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import tornado.ioloop
 5 import tornado.web
 6 from controllers.account import LoginHandler
 7 from controllers.home import HomeHandler
 8 import uimethods as mt  #导入UIMethod
 9 import uimodules as md  #导入UIModule
10 
11 #配置文件ul
12 settings = {
13     'template_path':'views',
14     'static_path':'static',
15     'static_url_prefix':'/static1/',  #设置前端静态文件名前缀
16     # Exception: You must define the 'cookie_secret' setting in your application to use secure cookies
17     'cookie_secret':'fsgfgdrhrthrthryryty',
18     'ui_methods':mt,#UIMethod注册
19     'ui_modules': md, #UIModule注册
20     'autoescape':None,#不将tab返回的字符串进行转译(阻止HTML格式渲染)
21 }
22 
23 #路由映射
24 #application对象中封装了:路由信息,配置信息
25 application = tornado.web.Application([
26     (r"/login.html", LoginHandler),
27     (r"/home.html", HomeHandler),
28 ],**settings)  #传入配置文件
29 
30 # application.add_handlers('buy.oldboy.com',[
31 #     (r"/login.html", LoginHandler),
32 #     (r"/index.html", MainHandler),
33 # ])
34 
35 if __name__ == "__main__":
36     # 创建socket对象 [socket,]
37     # sock = socket.socket()
38     # inputs = [socket,]
39     application.listen(8888)
40 
41     # 开启 r,w,e = select.select(inputs,)
42     tornado.ioloop.IOLoop.instance().start()
tornado_test.py
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 """
 5 账户相关信息
 6 """
 7 
 8 import tornado.web
 9 
10 class LoginHandler(tornado.web.RequestHandler):
11     def get(self,*args,**kwargs):
12         print(type(self.request)) #<class 'tornado.httpserver.HTTPRequest'>
13         from tornado.httpserver import HTTPRequest #查看
14         print(self._headers)  #获取请求头
15         # {'Content-Type': 'text/html; charset=UTF-8', 'Server': 'TornadoServer/1.2.1'}
16         self.render("login.html",msg="")  #render
17 
18     def post(self,*args,**kwargs):
19         # f = self.request.files['abc'] #获取文件
20         u = self.get_argument('username') #获取post提交的数据
21         p = self.get_argument('password')
22         print(u)
23         print(p)
24         # self.get_arguments()
25         if u == "root" and p == "123":
26             # self.set_cookie('is_login','true') #设置cookie
27             self.set_secure_cookie('is_login','true') #加密cookie
28             self.redirect('/home.html') #redirect
29         else:
30             self.render('login.html',msg="用户名或密码错误")
account.py
 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import tornado.web
 5 from tornado.httpserver import HTTPRequest
 6 class HomeHandler(tornado.web.RequestHandler):
 7     def get(self):
 8         # is_login = self.get_cookie('is_login') #获取cookie字符串
 9         is_login = self.get_secure_cookie('is_login') #获取加密cookie字符串
10         if not is_login:
11             self.redirect('/login.html')
12             return
13         self.write("欢迎登陆")  #相当于django的HttpResponse
home.py

2.Demo-Tornado内部提供基于cookie进行用户验证

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 import tornado.web
 4 
 5 class BaseHandler(tornado.web.RequestHandler):
 6     def get_current_user(self):
 7         return self.get_secure_cookie('is_login')
 8 
 9 class HomeHandler(BaseHandler):
10     @tornado.web.authenticated
11     def get(self):
12         # is_login = self.get_cookie('is_login') #获取cookie字符串
13         # is_login = self.get_secure_cookie('is_login') #获取加密cookie字符串
14         is_login = self.current_user
15         print(is_login)
16         if not is_login:
17             self.redirect('/login.html')
18             return
19         self.write(is_login)  #相当于django的HttpResponse
home.py

二、session

a.知识储备

 1.继承

 1 class C:
 2     def f1(self):
 3         print('c')
 4         # super(C,self).f1()
 5 class A(C):
 6     def f1(self):
 7         print('a')
 8         super(A,self).f1()
 9 
10 class B:
11     def f1(self):
12         print('B')
13 
14 class Foo(A,B):
15     pass
16 
17 obj = Foo()
18 obj.f1()
19 
20 #print ->>>> a c
继承

2.item系列方法

 1 class Foo(object):
 2 
 3     def __getitem__(self, key):
 4         print('__getitem__', key)
 5 
 6     def __setitem__(self, key, value):
 7         print('__setitem__', key, value)
 8 
 9     def __delitem__(self, key):
10         print('__delitem__', key)
11 
12 
13 obj = Foo()
14 result = obj['k1']
15 # obj['k2'] = 'wupeiqi'
16 # del obj['k1']
View Code

3.initialize

 1 class RequestHandler(object):
 2     """Base class for HTTP request handlers.
 3 
 4     Subclasses must define at least one of the methods defined in the
 5     "Entry points" section below.
 6     """
 7     SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PATCH", "PUT",
 8                          "OPTIONS")
 9 
10     _template_loaders = {}  # type: typing.Dict[str, template.BaseLoader]
11     _template_loader_lock = threading.Lock()
12     _remove_control_chars_regex = re.compile(r"[\x00-\x08\x0e-\x1f]")
13 
14     def __init__(self, application, request, **kwargs):
15         super(RequestHandler, self).__init__()
16 
17         self.application = application
18         self.request = request
19         self._headers_written = False
20         self._finished = False
21         self._auto_finish = True
22         self._transforms = None  # will be set in _execute
23         self._prepared_future = None
24         self._headers = None  # type: httputil.HTTPHeaders
25         self.path_args = None
26         self.path_kwargs = None
27         self.ui = ObjectDict((n, self._ui_method(m)) for n, m in
28                              application.ui_methods.items())
29         # UIModules are available as both `modules` and `_tt_modules` in the
30         # template namespace.  Historically only `modules` was available
31         # but could be clobbered by user additions to the namespace.
32         # The template {% module %} directive looks in `_tt_modules` to avoid
33         # possible conflicts.
34         self.ui["_tt_modules"] = _UIModuleNamespace(self,
35                                                     application.ui_modules)
36         self.ui["modules"] = self.ui["_tt_modules"]
37         self.clear()
38         self.request.connection.set_close_callback(self.on_connection_close)
39         self.initialize(**kwargs)
40 
41     def initialize(self):
42         """Hook for subclass initialization. Called for each request.
43 
44         A dictionary passed as the third argument of a url spec will be
45         supplied as keyword arguments to initialize().
46 
47         Example::
48 
49             class ProfileHandler(RequestHandler):
50                 def initialize(self, database):
51                     self.database = database
52 
53                 def get(self, username):
54                     ...
55 
56             app = Application([
57                 (r'/user/(.*)', ProfileHandler, dict(database=database)),
58                 ])
59         """
60         pass
initialize实例化执行

b.session实现机制

单继承应用

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import tornado.ioloop
 5 import tornado.web
 6 
 7 ########### 单继承示例 ###########
 8 class Foo(tornado.web.RequestHandler):
 9     def initialize(self):
10         self.A = 123
11         super(Foo,self).initialize()
12 
13 #查看RequestHandler -> __init__ -> self.initialize(**kwargs) #自定制方法
14 class MainHandler(Foo):
15     # def initialize(self): #自定制方法
16     #     self.A = 123
17 
18     def get(self):
19         print(self.A)
20         self.write("hello world")
21 
22 application = tornado.web.Application([
23     (r"/main.html", MainHandler),
24 ])
25 
26 
27 if __name__ == "__main__":
28 
29     application.listen(9999)
30     tornado.ioloop.IOLoop.instance().start()
单继承应用

多继承实现session原理

 1 #!/usr/bin/env python
 2 # -*- coding:utf-8 -*-
 3 
 4 import tornado.ioloop
 5 import tornado.web
 6 import time
 7 import hashlib
 8 
 9 container = {}
10 class Session(object):
11     def __init__(self,handler):
12         self.handler = handler
13         self.random_str = None
14 
15         #去用户请求信息中获取session_id,若果没有,新用户
16         client_random_str = self.handler.get_cookie('session_id')
17         if not client_random_str:  #判断是否存在
18             """新用户"""
19             self.random_str = self.create_random_str() #创建
20             container[self.random_str] = {}
21         else:
22             if client_random_str in container:  #判断对否正确
23                 """老用户"""
24                 self.random_str = client_random_str
25             else:
26                 """非法用户"""
27                 self.random_str = self.create_random_str() #错误则,新创建
28 
29         #2. 写入cookie(时间限制)
30         ctime = time.time()
31         self.handler.set_cookie('session_id', self.random_str,expires=ctime+1800)
32 
33     #1. 生成随机字符串
34     def create_random_str(self):
35         v = str(time.time())
36         m = hashlib.md5()
37         m.update(bytes(v,encoding='utf-8'))
38         return m.hexdigest()
39 
40     def __setitem__(self, key, value):
41         # 3. 后台存储
42         container[self.random_str][key] = value
43 
44     def __getitem__(self, item):
45         return container[self.random_str].get(item)
46 
47     def __delitem__(self, key):
48         del container[self.random_str][key]
49 
50     def clear(self):
51         del container[self.random_str]
52 
53 ########### 多继承示例 ###########
54 class Foo(object):
55     def initialize(self):  #自定制方法
56         # self.session = {}   #可以实现self.session['xx'] = "fsfsfsg" 设置值
57 
58         #self是MainHandler对象
59         # self.set_cookie() ->> MainHandler对象可以调用set_cookie方法
60         #将self传入Bar
61         self.session = Session(self)  #可以实现self.session['xx'] = "fsfsfsg" 设置值
62 
63         # self.A = 123
64         # super(Foo,self).initialize()
65 
66 #查看RequestHandler -> __init__ -> self.initialize(**kwargs) #自定制方法
67 class LoginHandler(Foo,tornado.web.RequestHandler):
68     def get(self):
69         # self.session['xx1'] = "shanshan001"  # __setitem__
70         # self.session['xx2'] = "shanshan002"  # __setitem__
71         # self.session['xx']             # __getitem__
72         # del self.session['xx']         # __delitem__
73 
74         # print(self.A)
75         # self.write("hello world")
76 
77         self.session['uuu'] = 'root'
78         self.redirect('/home.html')
79 
80 class HomeHandler(Foo,tornado.web.RequestHandler):
81     def get(self):
82         print("Hello")
83 
84         user = self.session['uuu']
85         if not user:
86             self.redirect("http://www.baidu.com")
87         else:
88             self.write(user)
89 
90 application = tornado.web.Application([
91     (r"/login.html", LoginHandler),
92     (r"/home.html", HomeHandler),
93 ])
94 
95 
96 if __name__ == "__main__":
97 
98     application.listen(9999)
99     tornado.ioloop.IOLoop.instance().start()
多继承实现session原理

自定义Session扩展

扩展前需了解:

 1 class Foo:
 2 
 3     def __contains__(self, item):
 4         print(item)
 5         return True
 6 
 7 obj = Foo()
 8 
 9 v = "x" in obj
10 
11 #运行结果:x
__contains__
  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 
  4 import tornado.ioloop
  5 import tornado.web
  6 import time
  7 import hashlib
  8 
  9 
 10 
 11 
 12 #操作数据源
 13 class Cache(object):
 14     """
 15     将session保存在内存
 16     """
 17     def __init__(self):
 18         self.container = {}
 19 
 20     def __contains__(self, item):       # cache = Cache()   "x" in cache  自动调用此方法
 21         return item in self.container
 22 
 23     def initial(self,random_str):
 24         self.container[random_str] = {}
 25 
 26     def get(self,random_str,key):
 27         return self.container[random_str].get(key)
 28 
 29     def set(self,random_str,key,value):
 30         self.container[random_str][key] = value
 31 
 32     def delete(self,random_str,key):
 33         del self.container[random_str][key]
 34 
 35     def open(self):
 36         pass
 37 
 38     def close(self):
 39         pass
 40 
 41     def clear(self,random_str):
 42         del self.container[random_str]
 43 
 44 class Memcache(object):
 45     """
 46     将session保存在硬盘
 47     """
 48     def __init__(self):
 49         pass
 50 
 51     def get(self,key):
 52         pass
 53 
 54     def set(self,key,value):
 55         pass
 56 
 57     def delete(self,key):
 58         pass
 59 
 60     def open(self):
 61         pass
 62 
 63     def close(self):
 64         pass
 65 
 66 class File(object):
 67     """
 68     将session保存在硬盘
 69     """
 70     def __init__(self):
 71         pass
 72 
 73     def get(self,key):
 74         pass
 75 
 76     def set(self,key,value):
 77         pass
 78 
 79     def delete(self,key):
 80         pass
 81 
 82     def open(self):
 83         pass
 84 
 85     def close(self):
 86         pass
 87 
 88 P = Cache
 89 # P = Memcache
 90 # P = File
 91 
 92 #操作
 93 class Session(object):
 94     def __init__(self,handler):
 95         self.handler = handler
 96         self.random_str = None
 97         self.db = P() #与操作数据源的类相关联
 98         self.db.open()
 99 
100         #去用户请求信息中获取session_id,若果没有,新用户
101         client_random_str = self.handler.get_cookie('session_id')
102         if not client_random_str:  #判断是否存在
103             """新用户"""
104             self.random_str = self.create_random_str() #创建
105             self.db.initial(self.random_str)
106         else:
107             if client_random_str in self.db:  #判断对否正确
108                 """老用户"""
109                 self.random_str = client_random_str
110             else:
111                 """非法用户"""
112                 self.random_str = self.create_random_str() #错误则,新创建
113                 self.db.initial(self.random_str)
114 
115         #2. 写入cookie(时间限制)
116         ctime = time.time()
117         self.handler.set_cookie('session_id', self.random_str,expires=ctime+1800)
118 
119         self.db.close()
120 
121     #1. 生成随机字符串
122     def create_random_str(self):
123         v = str(time.time())
124         m = hashlib.md5()
125         m.update(bytes(v,encoding='utf-8'))
126         return m.hexdigest()
127 
128     def __setitem__(self, key, value):
129         # 3. 后台存储
130         self.db.open()
131         self.db.set(self.random_str,key,value)
132         self.db.close()
133         # container[self.random_str][key] = value
134 
135     def __getitem__(self, item):
136         self.db.open()
137         v = self.db.get(self.random_str,item)
138         self.db.close()
139         return v
140         # return container[self.random_str].get(item)
141 
142     def __delitem__(self, key):
143         self.db.open()
144         self.db.delete(self.random_str,key)
145         self.db.close()
146         # del container[self.random_str][key]
147 
148     def clear(self):
149         self.db.open()
150         self.db.clear(self.random_str)
151         self.db.close()
152         # del container[self.random_str]
153 
154 ########### 多继承示例 ###########
155 class Foo(object):
156     def initialize(self):  #自定制方法
157         # self.session = {}   #可以实现self.session['xx'] = "fsfsfsg" 设置值
158 
159         #self是MainHandler对象
160         # self.set_cookie() ->> MainHandler对象可以调用set_cookie方法
161         #将self传入Bar
162         self.session = Session(self)  #可以实现self.session['xx'] = "fsfsfsg" 设置值
163 
164         # self.A = 123
165         # super(Foo,self).initialize()
166 
167 #查看RequestHandler -> __init__ -> self.initialize(**kwargs) #自定制方法
168 class LoginHandler(Foo,tornado.web.RequestHandler):
169     def get(self):
170         # self.session['xx1'] = "shanshan001"  # __setitem__
171         # self.session['xx2'] = "shanshan002"  # __setitem__
172         # self.session['xx']             # __getitem__
173         # del self.session['xx']         # __delitem__
174 
175         # print(self.A)
176         # self.write("hello world")
177 
178         self.session['uuu'] = 'root'
179         self.redirect('/home.html')
180 
181 class HomeHandler(Foo,tornado.web.RequestHandler):
182     def get(self):
183         print("Hello")
184 
185         user = self.session['uuu']
186         if not user:
187             self.redirect("http://www.baidu.com")
188         else:
189             self.write(user)
190 
191 application = tornado.web.Application([
192     (r"/login.html", LoginHandler),
193     (r"/home.html", HomeHandler),
194 ])
195 
196 
197 if __name__ == "__main__":
198 
199     application.listen(9999)
200     tornado.ioloop.IOLoop.instance().start()
数据源操作功能分离

c. Session框架

  1 #!/usr/bin/env python
  2 #coding:utf-8
  3 
  4 import sys
  5 import math
  6 from bisect import bisect
  7 
  8 
  9 if sys.version_info >= (2, 5):
 10     import hashlib
 11     md5_constructor = hashlib.md5
 12 else:
 13     import md5
 14     md5_constructor = md5.new
 15 
 16 
 17 class HashRing(object):
 18     """一致性哈希"""
 19     
 20     def __init__(self,nodes):
 21         '''初始化
 22         nodes : 初始化的节点,其中包含节点已经节点对应的权重
 23                 默认每一个节点有32个虚拟节点
 24                 对于权重,通过多创建虚拟节点来实现
 25                 如:nodes = [
 26                         {'host':'127.0.0.1:8000','weight':1},
 27                         {'host':'127.0.0.1:8001','weight':2},
 28                         {'host':'127.0.0.1:8002','weight':1},
 29                     ]
 30         '''
 31         
 32         self.ring = dict()
 33         self._sorted_keys = []
 34 
 35         self.total_weight = 0
 36         
 37         self.__generate_circle(nodes)
 38         
 39             
 40             
 41     def __generate_circle(self,nodes):
 42         for node_info in nodes:
 43             self.total_weight += node_info.get('weight',1)
 44             
 45         for node_info in nodes:
 46             weight = node_info.get('weight',1)
 47             node = node_info.get('host',None)
 48                 
 49             virtual_node_count = math.floor((32*len(nodes)*weight) / self.total_weight)
 50             for i in xrange(0,int(virtual_node_count)):
 51                 key = self.gen_key_thirty_two( '%s-%s' % (node, i) )
 52                 if self._sorted_keys.__contains__(key):
 53                     raise Exception('该节点已经存在.')
 54                 self.ring[key] = node
 55                 self._sorted_keys.append(key)
 56             
 57     def add_node(self,node):
 58         ''' 新建节点
 59         node : 要添加的节点,格式为:{'host':'127.0.0.1:8002','weight':1},其中第一个元素表示节点,第二个元素表示该节点的权重。
 60         '''
 61         node = node.get('host',None)
 62         if not node:
 63                 raise Exception('节点的地址不能为空.')
 64                 
 65         weight = node.get('weight',1)
 66         
 67         self.total_weight += weight
 68         nodes_count = len(self._sorted_keys) + 1
 69         
 70         virtual_node_count = math.floor((32 * nodes_count * weight) / self.total_weight)
 71         for i in xrange(0,int(virtual_node_count)):
 72             key = self.gen_key_thirty_two( '%s-%s' % (node, i) )
 73             if self._sorted_keys.__contains__(key):
 74                 raise Exception('该节点已经存在.')
 75             self.ring[key] = node
 76             self._sorted_keys.append(key)
 77         
 78     def remove_node(self,node):
 79         ''' 移除节点
 80         node : 要移除的节点 '127.0.0.1:8000'
 81         '''
 82         for key,value in self.ring.items():
 83             if value == node:
 84                 del self.ring[key]
 85                 self._sorted_keys.remove(key)
 86     
 87     def get_node(self,string_key):
 88         '''获取 string_key 所在的节点'''
 89         pos = self.get_node_pos(string_key)
 90         if pos is None:
 91             return None
 92         return self.ring[ self._sorted_keys[pos]].split(':')
 93     
 94     def get_node_pos(self,string_key):
 95         '''获取 string_key 所在的节点的索引'''
 96         if not self.ring:
 97             return None
 98             
 99         key = self.gen_key_thirty_two(string_key)
100         nodes = self._sorted_keys
101         pos = bisect(nodes, key)
102         return pos
103     
104     def gen_key_thirty_two(self, key):
105         
106         m = md5_constructor()
107         m.update(key)
108         return long(m.hexdigest(), 16)
109         
110     def gen_key_sixteen(self,key):
111         
112         b_key = self.__hash_digest(key)
113         return self.__hash_val(b_key, lambda x: x)
114 
115     def __hash_val(self, b_key, entry_fn):
116         return (( b_key[entry_fn(3)] << 24)|(b_key[entry_fn(2)] << 16)|(b_key[entry_fn(1)] << 8)| b_key[entry_fn(0)] )
117 
118     def __hash_digest(self, key):
119         m = md5_constructor()
120         m.update(key)
121         return map(ord, m.digest())
122 
123 
124 """
125 nodes = [
126     {'host':'127.0.0.1:8000','weight':1},
127     {'host':'127.0.0.1:8001','weight':2},
128     {'host':'127.0.0.1:8002','weight':1},
129 ]
130 
131 ring = HashRing(nodes)
132 result = ring.get_node('98708798709870987098709879087')
133 print result
134 
135 """
一致性哈希
 1 from hashlib import sha1
 2 import os, time
 3 
 4 
 5 create_session_id = lambda: sha1('%s%s' % (os.urandom(16), time.time())).hexdigest()
 6 
 7 
 8 class Session(object):
 9 
10     session_id = "__sessionId__"
11 
12     def __init__(self, request):
13         session_value = request.get_cookie(Session.session_id)
14         if not session_value:
15             self._id = create_session_id()
16         else:
17             self._id = session_value
18         request.set_cookie(Session.session_id, self._id)
19 
20     def __getitem__(self, key):
21         # 根据 self._id ,在一致性哈西中找到其对应的服务器IP
22         # 找到相对应的redis服务器,如: r = redis.StrictRedis(host='localhost', port=6379, db=0)
23         # 使用python redis api 链接
24         # 获取数据,即:
25         # return self._redis.hget(self._id, name)
26 
27     def __setitem__(self, key, value):
28         # 根据 self._id ,在一致性哈西中找到其对应的服务器IP
29         # 使用python redis api 链接
30         # 设置session
31         # self._redis.hset(self._id, name, value)
32 
33 
34     def __delitem__(self, key):
35         # 根据 self._id 找到相对应的redis服务器
36         # 使用python redis api 链接
37         # 删除,即:
38         return self._redis.hdel(self._id, name)
session

 总结:

 

1 -Session
2         1.
3             super按顺序查找
4             类名.方法名(self)
5             obj = Foo()   Foo.f1(obj)  or obj = Foo() obj.f1()
6         2.self是谁?永远是调用方法的对象
7         
8             单继承示例和多继承示例
9             #查看RequestHandler -> __init__ -> self.initialize(**kwargs) #自定制方法