原文作者: rfyiamcool 51CTO博客,原文链接:http://blog.51cto.com/rfyiamcool/1406378
为了提供让tornado更接近c10的能力,只能用nginx来处理tornado不太擅长的静态文件及用多app方案来提高负载能力。
我人比较的懒,把接口和平台的页面都做成一个py了,用upstream不好做负载,如果你用ip_hash,或者insert cookie的方式,虽然保证了针对后端服务器的命中,但是哥还就不想命中。
我还就想rr轮训,为啥? 因为页面上大量的耗时间的io和计算请求,这个时候我总是命中调度到一台服务器,那我就会一直的等待,后面还有一堆的任务也都在同步堵塞着。。。太痛快啦,这个时候就需要rr轮训,session如何的一致性,这个时候就需要一个快速的存储来保证session cookie的存储。
以前更多是用tornado memcached来存储session或者cookie,因为报警平台中已经在用redis、mongodb这些nosql数据库,没必要再配置memcached了。 这次用我钟爱的redis了。
这里导入了相关的类和库,login_required是装饰器,专门来判断用户登录了没有,没有的话把访问扔给login.html页面。
#xiaorui.cc
from base importBaseHandlerfrom tornado.web importHTTPErrordeflogin_required(f):def _wrapper(self,*args, **kwargs):printself.get_current_user()
logged=self.get_current_user()if logged ==None:
self.write('no login')
self.finish()else:
ret= f(self,*args, **kwargs)return_wrapperclassApplication(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"])
关联的两个类:
classMainHandler(BaseHandler):
@login_requireddefget(self):
username=self.get_current_user()print 'start..'
printusernameprint self.session['nima']if username==None:
self.write('nima')else:
self.write("What's up," + username + "?")classLoginHandler(BaseHandler):defget(self):
self.session["user_name"] = self.get_argument("name")
self.session["nima"] = 'xiaorui.cc'self.session.save()
self.write('你的session已经欧了')
处理session的文件 !
#/usr/bin/python#coding: utf-8
importuuidimporthmacimportujsonimporthashlibimportredisclassSessionData(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
classSession(SessionData):def __init__(self, session_manager, request_handler):
self.session_manager=session_manager
self.request_handler=request_handlertry:
current_session=session_manager.get(request_handler)exceptInvalidSessionException:
current_session=session_manager.get()for key, data incurrent_session.iteritems():
self[key]=data
self.session_id=current_session.session_id
self.hmac_key=current_session.hmac_keydefsave(self):
self.session_manager.set(self.request_handler, self)classSessionManager(object):def __init__(self, secret, store_options, session_timeout):
self.secret=secret
self.session_timeout=session_timeouttry: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'])exceptException as e:printedef_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({}):returnsession_dataelse:return{}exceptIOError:return{}def get(self, request_handler =None):if (request_handler ==None):
session_id=None
hmac_key=Noneelse:
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:raiseInvalidSessionException()
session=SessionData(session_id, hmac_key)ifsession_exists:
session_data=self._fetch(session_id)for key, data insession_data.iteritems():
session[key]=datareturnsessiondefset(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()))returnnew_id.hexdigest()def_generate_hmac(self, session_id):returnhmac.new(session_id, self.secret, hashlib.sha256).hexdigest()classInvalidSessionException(Exception):pass
tornado每个控制器相关的class ~
importtornado.webimportsysimportsessionclassBaseHandler(tornado.web.RequestHandler):def __init__(self, *argc, **argkw):
super(BaseHandler, self).__init__(*argc, **argkw)
self.session=session.Session(self.application.session_manager, self)defget_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就行了,匹配都在装饰器那边搞好了。
偶了,这就可以了。用之前要配置下相关的组件!
pip installujson redis
pipinstall tornado
session.py 代码来自:
git clone https://github.com/zs1621/tornado-redis-session
这老外写的有点简陋,说明几乎没有,还好tornado redis session本身就是不难的东西,看看就能搞定。
单个tornado我现在已经可以顶到1500个长连接不崩溃了,如果加上ngixn做tornado的分发负载,估计连接在6k问题不大。就算是接入所有业务的邮件转发问题也不大,估计问题都在邮件网关上了。