预备知识

  1. tornado框架session要自己写

  2. cookie存储在客户端浏览器上,session数据放在服务器上

  3. session依赖cookie

  4. 扩展tornado,返回请求前自定义session

  5. 面向对象的知识,obj['xxx']==>_getitems__,obj['xxx']=xxx ==> __setitems__, del obj['xxx'] ==> __delitems__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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实现机制

  1. tornado框架中,通过application的url映射到MainHandler类处理请求,根据请求的方式,通过反射执行对应的get或post等方法

  2. tornado源码中有很多扩展点(钩子)供开发者使用,如在执行get或post方法之前先执行initialiaze方法,即使没有调用也执行

  3. 扩展initialiaze,用户请求进来,读一下用户的cookie,如果有的话,能够拿到,没有可以创建一个

  4. 为避免重复在每个映射的类里都写一遍initialiaze,可以在他们的共同的基类BaseHandler(自定义的类)里写initialiaze方法

  5. 其实这些类应该继承tornado.web.RequestHandler类,让BaseHandler继承这个类,其他类也就继承了

  6. initialiaze中定义了self.my_session字段,它是一个session类的对象,init中获取或创建session

  7. 用户登录,通过__setitem__设置session中的内容,session保存在服务器中

  8. session_container是用来保存session的字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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)
        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):
        return session_container[self._id][key]
   
    def __setitem__(self, key, value):
        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):
        # my_session['k1']访问 __getitem__ 方法
        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'] = 'wupeiqi'
            self.my_session['c_card'] = '12312312309823012'
   
            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分布式

  1. session可以存在其它地方,如redis,数据库,mongo等等

  2. 当session规模达到很大数量级,就采用多台分布式存储,经过一致性hash算法,分配到对应的机器上

  3. 根据cookie字符串转换成一串数字,每台机器分管的数字段区域不同,权重大的管的大

一致性哈希​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/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)
                 
            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()
        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':1},
    {'host':'127.0.0.1:8001','weight':2},
    {'host':'127.0.0.1:8002','weight':1},
]
 
ring = HashRing(nodes)
result = ring.get_node('98708798709870987098709879087')
print result
 
"""
 
一致性哈希

session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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)

tornado--SESSION框架,一致性hash,分布式存储的更多相关文章

  1. Tornado自定义分布式session框架

    一.session框架处理请求执行的流程: 1.服务器端生成随机的cookie字符串 2.浏览器发送请求,服务器将cookie返回给浏览器. 3.服务器在生成一个字典.字典的key为cookie,va ...

  2. 搞懂分布式技术11:分布式session解决方案与一致性hash

    搞懂分布式技术11:分布式session解决方案与一致性hash session一致性架构设计实践 原创: 58沈剑 架构师之路 2017-05-18 一.缘起 什么是session? 服务器为每个用 ...

  3. 【转】分布式存储和一致性hash

    本文我将对一致性算法作介绍,同时谈谈自己对一致性hash和一般意义上的hash算法的区别 hash是什么 hash即hash算法,又称为散列算法,百度百科的定义是 哈希算法将任意长度的二进制值映射为较 ...

  4. Memcache 分布式存储 【一致性Hash】crc32

    class memcacheHash { private $_node = array(); private $_nodeData = array(); private $_keyNode = 0; ...

  5. tornado web 框架的认识

    tornado 简介 1,概述 Tornado就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本.Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的 ...

  6. 一致性hash应用到redis

    理解分布式存储的本质 有一个经典的实践经验: 数(值)据大了, 什么都是问题! 如果要求128B或更大数值计算, 哪么四则运算会是个大问题! 如果要求128T或更大日志存储, 哪么文件存储会是个大问题 ...

  7. 浅析tornado web框架

    tornado简介 1.tornado概述 Tornado就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本.Tornado 和现在的主流 Web 服务器框架(包括大多数 Py ...

  8. Tornado web 框架

    Tornado web 框架 其实很简单.深度应用 一.简介 Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本.这个 Web 框架看起来有些像we ...

  9. tornado web框架

    tornado web框架 tornado简介 1.tornado概述 Tornado就是我们在 FriendFeed 的 Web 服务器及其常用工具的开源版本.Tornado 和现在的主流 Web ...

随机推荐

  1. 在 Cloud 9 中搭建和运行 Go

    简介 自从使用了Chromebook,我脑中一直充斥着在云端开发的念头.在我使用过的位数不多的在线开发环境中,唯有 Cloud 9令我比较满意.实际上,Cloud 9还不支持Go的开发,因此本文我将教 ...

  2. 【腾讯Bugly干货分享】深度学习在OCR中的应用

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5809bb47cc5e52161640c5c8 Dev Club 是一个交流移动 ...

  3. 在英文版操作系统中安装的MS SQL server,中文字段无法匹配

    在英文版的操作系统中安装的MS SQL server,会出现中文字段无法被匹配到.其原因在于英文环境下安装的MS SQL server的排序规则不包括中文. 所以解决办法就是更改MS SQL serv ...

  4. 人人都是 DBA(VIII)SQL Server 页存储结构

    当在 SQL Server 数据库中创建一张表时,会在多张系统基础表中插入所创建表的信息,用于管理该表.通过目录视图 sys.tables, sys.columns, sys.indexes 可以查看 ...

  5. L#脚本语言,直接把DLL当脚本执行(图解说明)

    L#是什么:Run DLL as a Script. A Pure C# IL Runner,直接解析执行IL的脚本引擎. 从原理上讲是模拟执行了CLR的工作,从表现上讲就是把DLL作为资源直接加载执 ...

  6. 最全数据结构详述: List VS IEnumerable VS IQueryable VS ICollection VS IDictionary

    本文对常用的数据结构详述:Array, ArrayList,List,IList,ICollection, Stack, Queue, HashTable, Dictionary, IQueryabl ...

  7. springmvc下实现登录验证码功能

    总体思路,简单讲,就是后台生成图片同时将图片信息保存在session,前端显示图片,输入验证码信息后提交表单到后台,取出存放在session里的验证码信息,与表单提交的验证码信息核对. 点击验证码图片 ...

  8. React Native02-开始运行 Android篇

    1. 开始运行 1)用命令进入到新建的文件目录下,比如HelloWorld,再输入 react-native start: 在等待一段时间后,我们看到最后面有个地址,说明已经运行成功了. 我们输入地址 ...

  9. JS判断鼠标移入元素的方向

    最终效果 这里的关键主要是判断鼠标是从哪个方向进入和离开的 $("li").on("mouseenter mouseleave",function(e) { v ...

  10. 更新日志 - fir.im 新版优化上线

    经过这段时间的用户反馈收集和新版本的功能调研,我们对 fir.im Rio 上传下载.应用管理再次做了调整优化.感谢之前内测用户的反馈与建议.目前 fir.im Rio 新版已正式上线,主要优化有以下 ...