Tornado自定义分布式session框架
一、session框架处理请求执行的流程:
二、必备知识点
在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.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) # 根据自定义的值获取到客户端请求的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 == '': 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_farm
四、分布式实现
在前面的程序代码中,我们用的一个字典session_container = {},来存放客户端session相关的信息。这样做的缺点就是数据容易丢失。基于这个缺点,我们就可以把字典存放的方式改为拿专门的服务器来存放这些数据。如:redis、memcache等。但是如果只拿一台服务器来做这件事又会出现其他的缺点,如:宕机、负载过高等。所以,我们要在找出一个办法解决这个不足。
如上图所示,我们要实现多台机器同时运行来存放用户的session数据,首先生成一个哈希环。在这个环上存在几台机器的IP和权重。
当服务器对用户生成了新的cookie字符串时,我们得到这个字符串,经过一致性哈希算法得出一个值。然后与机器所设置的权重做对比,就可以确定要把这个用户的session信息放到哪一台服务器上。之后用户在次请求时,服务器就会根据用户发来的cookie经过计算后得知去哪一台服务器查找已经保存的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) # 获取每个节点的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 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
Tornado自定义分布式session框架的更多相关文章
- Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session框架
Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session import tornado.ioloop import tornado.web from myhas ...
- Tornado 自定义Form,session实现方法
一. 自定义Tornado 验证模块 我们知道,平时在登陆某个网站或软件时,网站对于你输入的内容是有要求的,并且会对你输入的错误内容有提示,对于Django这种大而全的web框架,是提供了form表单 ...
- 一个简单的分布式session框架
该代码只是用来学习原理的,有很多不完善之处. 代码: git@github.com:sicw/EasySpringSession.git 一. 整体设置 1. 实现Filter,封装新的reques ...
- 基于MongoDB打造.Net的分布式Session子系统
基于MongoDB打造.Net的分布式Session子系统 Taobao有她自己的分布式session框架,.net阵营也不能落后了,在下做了个基于MongoDB的支持最多26台MongoDB的分布式 ...
- net之session漫谈及分布式session解决方案
最近一直在纠结net下分布式会话的实现,现将近日来的个人感想记录如下,如果有什么更好的解决方案请指教. 1.什么是session: Session 对象存储特定用户会话所需的属性及配置信息.这样,当用 ...
- tornado 自定义session (一)
tornado 中没有session功能,需要我们自己实现. 目录: settings: settings = { 'template_path': 'templates', 'static': 's ...
- 基于ZooKeeper的分布式Session实现(转)
1. 认识ZooKeeper ZooKeeper—— “动物园管理员”.动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被 ...
- ASP.NET性能优化之分布式Session
如果我们正在使用Session,那么构建高性能可扩展的ASP.NET网站,就必须解决分布式Session的架构,因为单服务器的SESSION处理能力会很快出现性能瓶颈,这类问题也被称之为Session ...
- 基于ZooKeeper的分布式Session实现
1. 认识ZooKeeper ZooKeeper—— “动物园管理员”.动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被 ...
随机推荐
- 使用JS实现鼠标滚轮事件
网站需要实现鼠标滚轮滚一下,页面向下滑向下一个锚点,由于前面有个一样式必须用jQuery1.3.2,而好多滚轮事件都使用了更高版本的jQuery,于是就从网上找了找 <script type=& ...
- hbuilder工具快捷键 http://www.qq210.com/shoutu/android
http://www.qq210.com/shoutu/android 创建HTML结构: h 8 (敲h激活代码块列表,按8选择第8个项目,即HTML代码块,或者敲h t Enter)中途换行: ' ...
- 织梦DedeCMS网站地图模板
亲和百度蜘蛛,分页多层次特色,织梦系统最好用的网站地图! 用 DedeCMS(织梦) 系统搭建的网站多数都是以优化为主要目标的网站类型,既然是优化站 SEO 手段就离不开为网站设置网站地图.可是 De ...
- gentoo下的wpa_supplicant无线网配置
在linux使用wpa_supplicant获得无线网的最痛苦的是莫过于去配置wpa_supplicant.conf文件了(当然对于linux老手这不算什么), 但是可以用一种简便的方法直接输入命令行 ...
- CSS2书写顺序
1.位置属性(display, float, position, top, right, z-index, visibility,clear,list-style等) 2.自身属性(width, he ...
- 一个Highcharts的例子
关键字:Highcharts <!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Typ ...
- 【Python网络爬虫三】 爬去网页新闻
学弟又一个自然语言处理的项目,需要在网上爬一些文章,然后进行分词,刚好牛客这周的是从一个html中找到正文,就实践了一下.写了一个爬门户网站新闻的程序 需求: 从门户网站爬取新闻,将新闻标题,作者,时 ...
- Tomcat基础教程(四)
一.将Web应用部署到Tomcat中 为什么要部署?将Web应用部署到Tomcat中,那么Tomcat就能找到相应的Web应用,当Tomcat启动时就会加载和初始化Web应用,而在Tomcat启动后, ...
- HTML5之本地文件系统API - File System API
HTML5之本地文件系统API - File System API 新的HTML5标准给我们带来了大量的新特性和惊喜,例如,画图的画布Canvas,多媒体的audio和video等等.除了上面我们提到 ...
- 天草(初级+中级+高级)VIP和黑鹰VIP破解教程(全部iso下载地址)
以下就是我收集的教程地址,之前我收集到的都是一课一课下载的,虽然这样,我也下载完了天草的全部课程.这里分享的是在一起的iso文件,比起一课课下载爽多了.~~ 还有这些教程都是从零起点开始教的,不用担心 ...