Tornado自定义分布式session框架
来源:互联网 发布:tensorflow finetune 编辑:程序博客网 时间:2024/06/05 16:15
Tornado自定义分布式session框架
一、session框架处理请求执行的流程:
1、服务器端生成随机的cookie字符串
2、浏览器发送请求,服务器将cookie返回给浏览器。
3、服务器在生成一个字典。字典的key为cookie,value为另一个小字典。小字典就是为用户设置的字典
4、用户再次访问时,发送cookie到服务器端。服务器端收到cookie后,再去字典里查看一下其对应的值是否正确。
二、必备知识点
在Tornado的源码执行流程里,所有我们自定义的请求方法里都会继承一个基类:tornado.web.RequestHandler。这个类里有一个扩展点def initialize()。在tornado执行处理请求方法之前会先执行这里的方法。所以,我们可以利用此扩展点来实现session框架。
在对session操作时,需要面向对象特殊成员的一个知识点:
#!/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']
通过这个方法,我们就可以对session进行查找、创建、删除的操作。
三、代码实现
#!/usr/bin/env python# -*- coding:utf-8 -*-import tornado.ioloopimport tornado.webfrom hashlib import sha1import os, timesession_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) # 根据自定义的值获取到客户端请求的cookie if not session_value: # 如果没有说明是第一次请求,需要生成一个随机字符串当作cookie 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): # user = chenchap pwd = 123.com 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): 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'] = 'chenchao' self.my_session['c_card'] = '123.com' 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_container = {},来存放客户端session相关的信息。这样做的缺点就是数据容易丢失。基于这个缺点,我们就可以把字典存放的方式改为拿专门的服务器来存放这些数据。如:redis、memcache等。但是如果只拿一台服务器来做这件事又会出现其他的缺点,如:宕机、负载过高等。所以,我们要在找出一个办法解决这个不足。
如上图所示,我们要实现多台机器同时运行来存放用户的session数据,首先生成一个哈希环。在这个环上存在几台机器的IP和权重。
当服务器对用户生成了新的cookie字符串时,我们得到这个字符串,经过一致性哈希算法得出一个值。然后与机器所设置的权重做对比,就可以确定要把这个用户的session信息放到哪一台服务器上。之后用户在次请求时,服务器就会根据用户发来的cookie经过计算后得知去哪一台服务器查找已经保存的session信息。
#!/usr/bin/env python#coding:utf-8import sysimport mathfrom bisect import bisectif sys.version_info >= (2, 5): import hashlib md5_constructor = hashlib.md5else: import md5 md5_constructor = md5.newclass 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) # 获取每个节点的host 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() # md5加密 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':15}, {'host':'127.0.0.1:8001','weight':20}, {'host':'127.0.0.1:8002','weight':10},]ring = HashRing(nodes)result = ring.get_node('sdgsdg1s56g156gge56rgerg4')print result
我们可以通过设置每台机器的权重大小,来设计每台机器所承担的压力和重要性。
so.一开始的那段代码可以这么修改:
from hashlib import sha1import os, timecreate_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)
0 0
- Tornado自定义分布式session框架
- 为tornado自定义session
- Tornado框架
- Tornado框架
- tornado入门 - session
- spring-session实现分布式session共享及自定义sessionid
- 使用spring-session框架对会话session进行分布式管理
- 为tornado ,采用mongodb 加上session功能(web.py等 框架也可以参考本文)
- Django | Tornado | Flask 三种web框架的会话跟踪(session|cookie)
- tornado 自定义错误页面
- Tornado异步框架理解
- Tornado框架知识点
- Tornado Web 框架
- Tornado框架06-模板
- Tornado框架10-WebSocket
- Tornado框架11-部署
- Tornado框架07-数据库
- Tornado-Django-Flask框架...
- android 7.0 + 相机拍照 FileUriExposedException
- java折点计数
- Activity context 和 Application context 区别与联系
- GDB调试
- 观察者模式
- Tornado自定义分布式session框架
- compareTo方法和compare方法
- 认识Xcode的调试器:LLDB调试技术
- uva129||hdu 1627 Krypton Factor 回溯法
- Linux_Service—NFS
- random.nextInt()与Math.random()基础用法
- bzoj1078: [SCOI2008]斜堆
- MyBatis(二)
- 数据库oracle,pgsq,mysql.相关设置