tornado使用redis來實現(xiàn)session分布式存儲
前言:
為了提供讓tornado更接近c10的能力,只能用nginx來處理tornado不太擅長的靜態(tài)文件及用多app方案來提高負(fù)載能力。
我人比較的懶,把接口和平臺的頁面都做成一個py了,用upstream不好做負(fù)載,如果你用ip_hash,或者insert cookie的方式,雖然保證了針對后端服務(wù)器的命中,但是哥還就不想命中。
我還就想rr輪訓(xùn),為啥? 因為頁面上大量的耗時間的io和計算請求,這個時候我總是命中調(diào)度到一臺服務(wù)器,那我就會一直的等待,后面還有一堆的任務(wù)也都在同步堵塞著。。。太痛快啦,這個時候就需要rr輪訓(xùn),session如何的一致性,這個時候就需要一個快速的存儲來保證session cookie的存儲。
以前更多是用tornado memcached來存儲session或者cookie,因為報警平臺中已經(jīng)在用redis、mongodb這些nosql數(shù)據(jù)庫,沒必要再配置memcached了。 這次用我鐘愛的redis了。
這里導(dǎo)入了相關(guān)的類和庫,login_required是裝飾器,專門來判斷用戶登錄了沒有,沒有的話把訪問扔給login.html頁面。
- #xiaorui.cc
- from base import BaseHandler
- from tornado.web import HTTPError
- def login_required(f):
- def _wrapper(self,*args, **kwargs):
- print self.get_current_user()
- logged = self.get_current_user()
- if logged == None:
- self.write('no login')
- self.finish()
- else:
- ret = f(self,*args, **kwargs)
- return _wrapper
- class Application(tornado.web.Application):
- def __init__(self):
- settings = dict(
- cookie_secret = "e446976943b4e8442f099fed1f3fea28462d5832f483a0ed9a3d5d3859f==78d",
- session_secret = "3cdcb1f00803b6e78ab50b466a40b9977db396840c28307f428b25e2277f1bcc",
- session_timeout = 60,
- store_options = {
- 'redis_host': 'localhost',
- 'redis_port': 6379,
- 'redis_pass': '',
- },
- )
- handlers = [
- (r"/", MainHandler),
- (r"", MainHandler),
- (r"/login", LoginHandler)
- ]
- tornado.web.Application.__init__(self, handlers, **settings)
- self.session_manager = session.SessionManager(settings["session_secret"], settings["store_options"], settings["session_timeout"])
關(guān)聯(lián)的兩個類:
- class MainHandler(BaseHandler):
- @login_required
- def get(self):
- username = self.get_current_user()
- print 'start..'
- print username
- print self.session['nima']
- if username==None:
- self.write('nima')
- else:
- self.write("What's up, " + username + "?")
- class LoginHandler(BaseHandler):
- def get(self):
- self.session["user_name"] = self.get_argument("name")
- self.session["nima"] = 'xiaorui.cc'
- self.session.save()
- self.write('你的session已經(jīng)歐了')
處理session的文件 !
- #/usr/bin/python
- # coding: utf-8
- import uuid
- import hmac
- import ujson
- import hashlib
- import redis
- class SessionData(dict):
- def __init__(self, session_id, hmac_key):
- self.session_id = session_id
- self.hmac_key = hmac_key
- # @property
- # def sid(self):
- # return self.session_id
- # @x.setter
- # def sid(self, value):
- # self.session_id = value
- class Session(SessionData):
- def __init__(self, session_manager, request_handler):
- self.session_manager = session_manager
- self.request_handler = request_handler
- try:
- current_session = session_manager.get(request_handler)
- except InvalidSessionException:
- current_session = session_manager.get()
- for key, data in current_session.iteritems():
- self[key] = data
- self.session_id = current_session.session_id
- self.hmac_key = current_session.hmac_key
- def save(self):
- self.session_manager.set(self.request_handler, self)
- class SessionManager(object):
- def __init__(self, secret, store_options, session_timeout):
- self.secret = secret
- self.session_timeout = session_timeout
- try:
- if store_options['redis_pass']:
- self.redis = redis.StrictRedis(host=store_options['redis_host'], port=store_options['redis_port'], password=store_options['redis_pass'])
- else:
- self.redis = redis.StrictRedis(host=store_options['redis_host'], port=store_options['redis_port'])
- except Exception as e:
- print e
- def _fetch(self, session_id):
- try:
- session_data = raw_data = self.redis.get(session_id)
- if raw_data != None:
- self.redis.setex(session_id, self.session_timeout, raw_data)
- session_data = ujson.loads(raw_data)
- if type(session_data) == type({}):
- return session_data
- else:
- return {}
- except IOError:
- return {}
- def get(self, request_handler = None):
- if (request_handler == None):
- session_id = None
- hmac_key = None
- else:
- session_id = request_handler.get_secure_cookie("session_id")
- hmac_key = request_handler.get_secure_cookie("verification")
- if session_id == None:
- session_exists = False
- session_id = self._generate_id()
- hmac_key = self._generate_hmac(session_id)
- else:
- session_exists = True
- check_hmac = self._generate_hmac(session_id)
- if hmac_key != check_hmac:
- raise InvalidSessionException()
- session = SessionData(session_id, hmac_key)
- if session_exists:
- session_data = self._fetch(session_id)
- for key, data in session_data.iteritems():
- session[key] = data
- return session
- def set(self, request_handler, session):
- request_handler.set_secure_cookie("session_id", session.session_id)
- request_handler.set_secure_cookie("verification", session.hmac_key)
- session_data = ujson.dumps(dict(session.items()))
- self.redis.setex(session.session_id, self.session_timeout, session_data)
- def _generate_id(self):
- new_id = hashlib.sha256(self.secret + str(uuid.uuid4()))
- return new_id.hexdigest()
- def _generate_hmac(self, session_id):
- return hmac.new(session_id, self.secret, hashlib.sha256).hexdigest()
- class InvalidSessionException(Exception):
- pass
tornado每個控制器相關(guān)的class ~
- import tornado.web
- import sys
- import session
- class BaseHandler(tornado.web.RequestHandler):
- def __init__(self, *argc, **argkw):
- super(BaseHandler, self).__init__(*argc, **argkw)
- self.session = session.Session(self.application.session_manager, self)
- def get_current_user(self):
- return self.session.get("user_name")
對于登錄注冊session:
- self.session["user_name"] = self.get_argument("name")
- self.session["nima"] = 'xiaorui.cc'
- self.session.save()
對于退出登錄:
- self.session["nima"] =None
- self.session.save()
其實就改成None就行了,匹配都在裝飾器那邊搞好了。
原文:http://rfyiamcool.blog.51cto.com/1030776/1406378
偶了,這就可以了。用之前要配置下相關(guān)的組件!
pip install ujson redis
pip install tornado
session.py 代碼來自:
- git clone https://github.com/zs1621/tornado-redis-session
這老外寫的有點簡陋,說明幾乎沒有,還好tornado redis session本身就是不難的東西,看看就能搞定。
單個tornado我現(xiàn)在已經(jīng)可以頂?shù)?500個長連接不崩潰了,如果加上ngixn做tornado的分發(fā)負(fù)載,估計連接在6k問題不大。就算是接入所有業(yè)務(wù)的郵件轉(zhuǎn)發(fā)問題也不大,估計問題都在郵件網(wǎng)關(guān)上了。
博客地址:http://rfyiamcool.blog.51cto.com/1030776/1406378