Tornado自定义分布式session框架
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__',keydef __setitem__(self, key, value):print '__setitem__',key,valuedef __delitem__(self, key):print '__delitem__',keyobj = 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, 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) # 根据自定义的值获取到客户端请求的cookieif not session_value: # 如果没有说明是第一次请求,需要生成一个随机字符串当作cookieself._id = create_session_id()else:self._id = session_valuerequest.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.comif session_container.has_key(self._id):session_container[self._id][key] = valueelse: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 sys import math from bisect import bisectif sys.version_info >= (2, 5):import hashlibmd5_constructor = hashlib.md5 else:import md5md5_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 = 0self.__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] = nodeself._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 += weightnodes_count = len(self._sorted_keys) + 1virtual_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] = nodeself._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 Nonereturn self.ring[self._sorted_keys[pos]].split(':')def get_node_pos(self,string_key):'''获取 string_key 所在的节点的索引'''if not self.ring:return Nonekey = self.gen_key_thirty_two(string_key)nodes = self._sorted_keyspos = bisect(nodes, key) # 根据一个列表和加密的字符串计算出一个数值return posdef 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, 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_valuerequest.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)
Tornado自定义分布式session框架相关推荐
- 分布式Session框架
分布式Session框架 配置服务器,Zookeeper集群管理服务器可以统一管理所有服务器的配置文件 共享这些Session存储在一个分布式缓存中,可以随时写入和读取,而且性能要很好,如Memcac ...
- Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session框架
Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session import tornado.ioloopimport tornado.webfrom myhash ...
- 为tornado自定义session
cookie和session 在自定义session前,我们需要先了解cookie和session是什么,可以参考我之前的博客:http://blog.csdn.net/ayhan_huang/art ...
- 详解比springSecurity和shiro更简单优雅的轻量级Sa-Token框架,比如登录认证,权限认证,单点登录,OAuth2.0,分布式Session会话,微服务网关鉴权
文章目录 1. 技术选型 2. Sa-Token概述 2.1 简单介绍 2.2 登录认证 2.3 权限认证 3. 功能一览 4. Sa-Token使用 4.1 引入Sa-Token依赖 4.2 Sa- ...
- 一个自定义python分布式爬虫框架。
一个分布式爬虫框架.比scrapy简单很多,不需要各种item pipeline middwares spider settings run文件之间来回切换写代码,这只需要一个文件,开发时候可以节约很 ...
- 170222、使用Spring Session和Redis解决分布式Session跨域共享问题
使用Spring Session和Redis解决分布式Session跨域共享问题 原创 2017-02-27 徐刘根 Java后端技术 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用 ...
- ASP.NET WebApi 基于分布式Session方式实现Token签名认证
一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...
- 使用 做签名的post_ASP.NET WebApi 基于分布式Session方式实现Token签名认证
一.课程介绍 明人不说暗话,跟着阿笨一起玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebServ ...
- 基于ZooKeeper的分布式Session实现
基于ZooKeeper的分布式Session实现 [转 http://blog.csdn.net/jacktan/article/details/6112806] 认识ZooKeeper ZooKee ...
最新文章
- 《麻省理工科技评论》:2018年18大科技趋势,2017年7大失败技术
- mysql注入式攻击_SQL的注入式攻击方式和避免方法
- Codeforces Round #539 Div. 1
- MySQL客户端连接被频繁杀掉,企业案例(一):由于mysql sleep线程过多小故障
- Android----获取包名和sh1
- MySQL中针对大数据量常用技术
- window 清理maven本地仓库
- 灵感专题—2019年优秀网页设计作品赏析#5月
- 为什么有的锂电保护板需要激活之锂电池保护板怎么激活
- Spring 动态代理
- 实体书回暖?码洋过10,000,000的随想
- 随机生成三位密码,然后穷举法破解密码
- Ubuntu生成so共享库的方法
- Linux中断管理 (3)workqueue工作队列
- [GoFrame学习] 报错 implement not found for interface IMenu, forgot register?
- WMI使用小工具——WMI代码生成器(转)
- 牛客网 NC204859 组队 滑动窗口
- 循环结构中“当型”与“直到型”判断的理解
- 【数据结构】迷宫问题实现(包含界面)
- Numerical Optimization之Nonlinear Equations
热门文章
- 开发者都想收藏的深度学习脑图,我们抢先曝光了!
- 大数据中台向AI中台演进是大势所趋?
- 如何用TF Object Detection API训练交通信号灯检测神经网络?
- 算法面试经常需要你手写的三个排序算法(Python语言)
- 英伟达Q4净利同比降49%,还能继续躺赚吗?
- 资源 | 斯坦福最新NLP课程上线,选择PyTorch放弃TensorFlow
- 月薪30k~50k,这个领域的人才正在被疯抢!
- 【活动】人工智能产学研生态建设研讨会报名
- 资源 | 10x Python开发者必读:本月Python文章TOP 10
- 用了这么久 IDEA,你竟然不知道有个功能叫自动补全!