论坛首页 编程语言技术论坛

为tornado ,采用mongodb 加上session功能(web.py等 框架也可以参考本文)

浏览 7776 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (4) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-08-14  
1, session的原理
http是无状态协议, 理论上说,无法管理和跟踪客户端信息,1994年网景公司,在http的基础上增加了cookie,用于在客户端保存客户信息,后来cookie也称了http的标准。
session是建立在cookie的基础之上, 实现方法为, 在客户端使用cookie保存session_id, http serve 从cookie得到session_id后,通过session_id得到客户的对应信息。 (至于怎么得到, 以及session怎么保存,取决怎么实现)

2, tornado http server 为一个基于epoll的高性能http 服务, 有facebook 捐献, 被广泛的用于facebook(facebook的http服务,都由tornado提供)

3,tonado上实现session功能 (session 采用mongodb保存)

思路如下,
1, 从cookie中获取session_id, 如果有的话,就从mongodb中load对应的sessin
2, 如果没有, 创建空session
3, 如果当前没有session, 保存时,需要通过cookie设置 session_id

模块
1, SessionManager sessin管理,负责保存session, load session ,生成唯一的session_id等功能
2, BaseSession session的基础实现

本案例sessionManager 采用mongoDB为后端

## --------------------------------------------

# session manager

class SessionManagerBase(object):
    """session manager的基类"""
    def generate_session_id(self, salt):
        """生成唯一的session_id"""
        rand = os.urandom(16)
        now = time.time()
        return sha1("%s%s%s" %(rand, now, salt)).hexdigest()

    def create_new(self, session_id):
        """创建空session,当session不存在时"""
        pass

    def save_session(self, session):
        """保存session"""
        pass

    def load_session(self, session_id = None):
        """根据session_id load session"""
        pass


## --------------------------------------------

class MongoSessionManager(SessionManagerBase):
    def __init__(self, db, collection_name='sessions', **kw):
        """session 采用mongodb为后端保存, 默认是存在 sessions 集合中"""
        self._collection = db[collection_name]

    def create_new(self, session_id):
        return BaseSession(session_id, self, {})

    def save_session(self, session):
        """保存session 到mongodb"""
        self._collection.save({'_id' : session.get_session_id(), 'data' : session})

    def load_session(self, session_id = None):
        data = {} # 默认为空session
        if session_id:
            # 有session ,就调入
            session_data = self._collection.find_one({'_id' : session_id})
            if session_data:
                # 防止错误数据
                data = session_data['data']

        return BaseSession(session_id, self, data)

## --------------------------------------------
# session

class BaseSession(dict):
    def __init__(self, session_id = '', mgr = None, data = {}):
        self.__session_id = session_id
        self.__mgr = mgr
        self.update(data)
        self.__change = False # 小小的优化, 如果session没有改变, 就不用保存了

    def get_session_id(self):
        return self.__session_id
  
    def save(self):
        if self.__change:
            self.__mgr.save_session(self)
            self.__change = False

    # ------------------------------------------
    # 使用session[key] 当key不存在时返回None, 防止出现异常
    def __missing__(self, key):
        return None

    def __delitem__(self, key):
        if key in self:
            del self[key]
            self.__change = True

    def __setitem__(self, key, val):
        self.__change = True
        super(BaseSession, self).__setitem__(key, val)
        



以上为sessionmanager 和 basesession的代码, 看看注释,就能明白了。

3 使用
有2中方法, 1, 继承tornado 的 RequestHandler, 在子类中实现get_session 这个方法
实现方便,但这个方式需要引入新的类,而且session 代码需要和特定服务器耦合,如果需要请自己实现。

在此要讨论的是另一种思路,采用函数装饰来实现

session_settings 保存session的配置, 整个session的配置会放在RequestHandler的settings中

def session(func):
    @functools.wraps(func)
    def warpper(self, *args, **kwargs):
        self.require_setting('session_settings', 'session')
        # 获取session配置信息
        session_settings = self.settings['session_settings']
        # 取得session manager
        mgr = session_settings['mgr']

        #通过cookie 得到session_id
        cookie_name = session_settings['cookie_name']
        session_id = self.get_secure_cookie(cookie_name)

        # 得到session ,并保存在request.session中
        if session_id:
            #如果有session 直接调出
            setattr(self, 'session', mgr.load_session(session_id))
        else:
            # 当前没有session ,需要保存session_id到cookie中, 并返回新建的空session
            secret_key = session_settings['secret_key']
            session_id = mgr.generate_session_id(secret_key)
            self.set_secure_cookie(cookie_name, session_id)
            setattr(self, 'session', mgr.create_new(session_id))

        return_val = func(self, *args, **kwargs)

        # 在被装饰的函数执行后, 保存session
        self.session.save()

        return return_val
    
    return warpper


4, 使用

# -*- coding: utf-8 -*-

# ---------------------------
#DB

from xanadu import MongoSessionManager
from xanadu.nosql import *

db = open_db('10.45.12.197')

test_db = db('test')
session_mgr = MongoSessionManager(test_db)


# ---------------------------
# settings

cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo="

session_settings = {
    'mgr' : session_mgr,
    'cookie_name': 'session_id',
    'cookie_domain': None,
    'cookie_expires': 86400, #24 * 60 * 60, # 24 hours in seconds
    'ignore_expiry': True,
    'ignore_change_ip': True,
    'secret_key': cookie_secret,
    'expired_message': 'Session expired',
    'httponly': True
}

dbs = {
    'default' : test_db,
}

db_sessions = {
    'default' = open_session(test_db)
}

static_path = 'static'

# ---------------------------


from xanadu import test
from xanadu import RequestHandlerEx
from xanadu.ctl import *

import tornado

class HelloHandler(RequestHandlerEx):
    def get(self):
        self.write('Hello')

application = tornado.web.Application(
    [
        (r"/login", LoginHandler),
        (r"/test_session", test.session.TestSessionHandler2),
        (r"/hello", HelloHandler),
    ],
    **locals()
)
   发表时间:2011-08-30  
好文啊!以前做过mongo替换jetty的,tonado,mark了。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics