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__',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框架相关推荐

  1. 分布式Session框架

    分布式Session框架 配置服务器,Zookeeper集群管理服务器可以统一管理所有服务器的配置文件 共享这些Session存储在一个分布式缓存中,可以随时写入和读取,而且性能要很好,如Memcac ...

  2. Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session框架

    Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session import tornado.ioloopimport tornado.webfrom myhash ...

  3. 为tornado自定义session

    cookie和session 在自定义session前,我们需要先了解cookie和session是什么,可以参考我之前的博客:http://blog.csdn.net/ayhan_huang/art ...

  4. 详解比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- ...

  5. 一个自定义python分布式爬虫框架。

    一个分布式爬虫框架.比scrapy简单很多,不需要各种item pipeline middwares spider settings run文件之间来回切换写代码,这只需要一个文件,开发时候可以节约很 ...

  6. 170222、使用Spring Session和Redis解决分布式Session跨域共享问题

    使用Spring Session和Redis解决分布式Session跨域共享问题 原创 2017-02-27 徐刘根 Java后端技术 前言 对于分布式使用Nginx+Tomcat实现负载均衡,最常用 ...

  7. ASP.NET WebApi 基于分布式Session方式实现Token签名认证

    一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...

  8. 使用 做签名的post_ASP.NET WebApi 基于分布式Session方式实现Token签名认证

    一.课程介绍 明人不说暗话,跟着阿笨一起玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebServ ...

  9. 基于ZooKeeper的分布式Session实现

    基于ZooKeeper的分布式Session实现 [转 http://blog.csdn.net/jacktan/article/details/6112806] 认识ZooKeeper ZooKee ...

最新文章

  1. 《麻省理工科技评论》:2018年18大科技趋势,2017年7大失败技术
  2. mysql注入式攻击_SQL的注入式攻击方式和避免方法
  3. Codeforces Round #539 Div. 1
  4. MySQL客户端连接被频繁杀掉,企业案例(一):由于mysql sleep线程过多小故障
  5. Android----获取包名和sh1
  6. MySQL中针对大数据量常用技术
  7. window 清理maven本地仓库
  8. 灵感专题—2019年优秀网页设计作品赏析#5月
  9. 为什么有的锂电保护板需要激活之锂电池保护板怎么激活
  10. Spring 动态代理
  11. 实体书回暖?码洋过10,000,000的随想
  12. 随机生成三位密码,然后穷举法破解密码
  13. Ubuntu生成so共享库的方法
  14. Linux中断管理 (3)workqueue工作队列
  15. [GoFrame学习] 报错 implement not found for interface IMenu, forgot register?
  16. WMI使用小工具——WMI代码生成器(转)
  17. 牛客网 NC204859 组队 滑动窗口
  18. 循环结构中“当型”与“直到型”判断的理解
  19. 【数据结构】迷宫问题实现(包含界面)
  20. Numerical Optimization之Nonlinear Equations

热门文章

  1. 开发者都想收藏的深度学习脑图,我们抢先曝光了!
  2. 大数据中台向AI中台演进是大势所趋?
  3. 如何用TF Object Detection API训练交通信号灯检测神经网络?
  4. 算法面试经常需要你手写的三个排序算法(Python语言)
  5. 英伟达Q4净利同比降49%,还能继续躺赚吗?
  6. 资源 | 斯坦福最新NLP课程上线,选择PyTorch放弃TensorFlow
  7. 月薪30k~50k,这个领域的人才正在被疯抢!
  8. 【活动】人工智能产学研生态建设研讨会报名
  9. 资源 | 10x Python开发者必读:本月Python文章TOP 10
  10. 用了这么久 IDEA,你竟然不知道有个功能叫自动补全!