Flask 进阶session和上下文管理解析

时间:2023-03-08 20:13:43

session的源码流程

Flask 进阶session和上下文管理解析

将session放置在redis中

安装

pip install flask-session

使用

import redis
from flask import Flask,request,session
from flask.sessions import SecureCookieSessionInterface
from flask_session import Session app = Flask(__name__)
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.Redis(host='140.143.227.206',port=6379,password='')
Session(app) @app.route('/login')
def login():
session['user'] = 'alex'
return 'asdfasfd' @app.route('/home')
def index():
print(session.get('user')) return '...' if __name__ == '__main__':
app.run()

上下文管理基础

上下文管理之基础threading.local

1.threading.local

  • 实例化一个local对象
  • obj.xxxx=i会触发__setattr__ 设置值
  • obj.xxxx的会触发__getattr__取值
  • local能为每一个线程开辟一个新的空间,让每个线程的资源相互不干扰
import threading,time
from threading import local
obj=local()
def task(i):
obj.xxxxx = i
time.sleep(2)
print(obj.xxxxx,i) for i in range(0,10):
t=threading.Thread(target=task,args=(i,))
t.start()

2.用字典替换local

import threading,time
from threading import local obj=local()
DIC={}
def task(i):
ident = threading.get_ident()
if ident in DIC:
DIC[ident]['xxxx']=i
else:
DIC[ident]={'xxxx':i}
time.sleep(2)
print(DIC[ident]['xxxx'],i,ident)
for i in range(0,10):
t=threading.Thread(target=task,args=(i,))
t.start()
  • ident = threading.get_ident() 获取线程的唯一标识

  • 将所有的数据根据线程唯一标示为key存放到字典里,那么该方法也可以避免线程资源冲突...因为DIC是一个全部变量字典

3. 重写local支持协程

import threading,time,greenlet
try:
get_ident=greenlet.getcurrent
except Exception as e:
get_ident=threading.get_ident
class Local(object):
DIC={}
def __getattr__(self, item):
ident = get_ident()
print(ident)
if ident in self.DIC:
return self.DIC[ident].get(item)
return None
def __setattr__(self, key, value):
ident= get_ident()
if ident in self.DIC:
self.DIC[ident][key]=value
else:
self.DIC[ident]={key:value}
obj=Local()
def task(i):
obj.ha=i
time.sleep(2)
print(obj.ha)
for i in range(0,10):
t= threading.Thread(target=task,args=(i,))
t.start()

上下文管进阶理解

1.偏函数

# by luffycity.com
import functools
def index(a1,a2):
return a1 + a2
# 原来的调用方式
# ret = index(1,23)
# print(ret)
# 偏函数,帮助开发者自动传递参数
new_func = functools.partial(index,666)
ret = new_func(1)
print(ret)

2.执行父类方法

class Base(object):
def func(self):
print('Base.func')
class Foo(Base):
def func(self):
# 方式一:根据mro的顺序执行方法
# super(Foo,self).func()
# 方式二:主动执行Base类的方法
# Base.func(self) print('Foo.func')
obj = Foo()
obj.func()
# print(Foo.__mro__)
 

3.object.__setattr__

class Foo(object):
def __init__(self):
# self.storage = {}
# 在父类的setattr设置storage为空列表
object.__setattr__(self,'storage',{})
def __getattr__(self, item):
print item def __setattr__(self, key, value):
print(key,value,self.storage) obj = Foo()
obj.xx = 123
  • obj=Foo() 实例化对象会执行,__new__......再执行__init__方法加上object.__setattr__会执行object类的方法设置值
  • obj.xx=123设置值会执行该类的__setattr__

4.Flask  Local源码

  • local是用来给每个线程或者协程开辟一个空间
try:
from greenlet import getcurrent as get_ident
except:
from threading import get_ident
class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
# __storage__ = {1231:{'stack':[]}}
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)

5.LocalStack源码

  • localstack 维护local的列表,维护成一个栈
class LocalStack(object):
def __init__(self):
self._local = Local() def push(self,value):
rv = getattr(self._local, 'stack', None) # self._local.stack =>local.getattr
if rv is None:
self._local.stack = rv = [] # self._local.stack =>local.setattr
rv.append(value) # self._local.stack.append(666)
return rv def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
return stack[-1]
else:
return stack.pop() def top(self):
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None

源码入口

app.__call__
app.wsgi_app

上下文管理request简易流程图

Flask 进阶session和上下文管理解析

上下文管理request,session流程

  • -请求到来之后会触发__call__方法,由__call__方法再次调用wsgi__app方法
  • -在wsgi_app方法中:
  • -首先将请求相关+空session封装到一个RequestContext对象中即:ctx
  • -将ctx交给LocalStack对象,再由LocalStack将ctx添加到local中,Local结构:
  • __storage__={
  • 线/协程的唯一标识:{stack:[ctx,]}
  • -根据请求中的cookie中提取铭文sessionid对应的值,对cookie进行加密+反序列化
  • ->视图函数
  • -把session中的数据再次加密序列化写入到cookie中.
  • -将ctx删除
  • -结果返回给用户浏览器
  • -断开socket连接.
    #LocalStack是全局的 单例模式
    _request_ctx_stack = LocalStack()
    _app_ctx_stack = LocalStack()
    request = LocalProxy(partial(_lookup_req_object, 'request'))
    session = LocalProxy(partial(_lookup_req_object, 'session'))

上下文管理最终流程图

Flask 进阶session和上下文管理解析