flask分布式部署session的保存方案
-
flask
默认的session
是怎么实现的
flask
作为 web
应用框架若多机部署,第一个问题是需要一个请求接入网关,通常我们使用 nginx
统一进行流量的分发。
但随之而来会有一个新的问题,即 flask
的 session
多机之间会共享吗?带着这个问题,我们看看 flask
关于 session
的源码:
class (CallbackDict, SessionMixin):
...
class SecureCookieSessionInterface(SessionInterface):
def open_session(self, app, request):
...
def save_session(self, app, session, response):
...
以上是 flask.sessions.py
实现的主要框架:
SecureCookieSession
即 flask
的 session
类,可以简单的理解成一个 dict
对象。
SecureCookieSessionInterface
即 flask
的 session
接口类,open_session
方法用于创建 session
,save_session
方法用于将 session
加密并存放在 response
的 cookie
中。所以 flask
是默认将用户的 session
存储在客户端的 cookie
中,这样请求 - 应答的数据中就有了用户操作的上下文了,至于这么做的优劣将在下文分析。
-
常见的分布式部署 session 解决方案
1.服务器间 session
复制
session
复制是早期的企业级的使用比较多的一种服务器集群sessio
n 管理机制。应用服务器开启web
容器的session
复制功能,在集群中的几台 服务器之间同步session
对象,使得每台服务器上都保存所有的session
信息,这样任何一台宕机都不会导致session
的数据丢失,服务器使用session
时,直接从本地获取。
像java 的一些应用服务器,如
tomcat等自带次功能。在
python-web不常见 **缺点:**是
session` 同步会暂用内网网络带宽,且服务器水平扩展存在明显上线。
2.session
与服务器绑定
通过请求网关,如
nginx
,将负载均衡的策略改成ip-hash
的模式,即用户的每次请求都会分发到同一台服务器,那么sesison
则能够正常的被解析。
优点:无需修改业务代码
缺点:缺乏高可用性,当其中一台服务器宕机,该机器上用户需要重新登录到其他服务器
3.客户端 session
存储
即
flask
默认的session
存储方案,可见什么都不需要改动,flask
已经支持水平扩展,细心的童鞋想想当flask
通过gunicorn
启动时多进程为啥能够共享session
,即不难想到多个服务间共享应该也问题不大。这里注意的是多服务器间的secret_key
必须相同。
优点:无需改造,flask 默认支持
缺点:
①session
数据存储在客户端,即使加密也还是一件存在泄露风险的事情session
数据占用外网带宽
②受cookie
的大小限制,session
能记录的数据有限
4.服务端 session
统一存储
对
session
进行统一的存储,所有服务器共享该存储服务上的数据
优点:服务水平扩展性良好,服务端存储,安全
缺点:每次请求至少需要一次内部网络请求,占用网络带宽
需要侵入业务代码
-
flask-session 服务端 session 存储
通过比较不难发现,服务端 session
统一存储是最合适的解决方案。
那么我们来谈谈怎么实现,幸运的是已经有前任实现了 flask
对应的扩展包flask-session
,我们一起看看它的实现:
代码大概 500+ 行,但我们实际用到的可能就几十行。
首先我们需要选择 session
寄存的服务,flask-session
支持redis
,memcached
,filesystem
,mongodb
,sqlalchemy
作为存储介质
以 redis
举例,再看代码:
class RedisSessionInterface(SessionInterface):
serializer = pickle
session_class = RedisSession
def __init__(self, redis, key_prefix, use_signer=False, permanent=True):
if redis is None:
from redis import Redis
redis = Redis()
self.redis = redis
self.key_prefix = key_prefix
self.use_signer = use_signer
self.permanent = permanent
def open_session(self, app, request):
...
def save_session(self, app, session, response):
...
重写open_session
,save_session
,将 session(dict)
存储在 redis
并将 session_id(key)
返回给客户端
-
flask http 请求 - 应答完整的数据流
客户端 http 请求
-> 服务端负载均衡至随机服务器
-> 应用上下文入栈 (app_ctx
)
-> 请求上下文入栈 (request_ctx
),同时生成 session
-> 通过 request_ctx
中的路由信息找到视图函数 (view_func
)
-> view_func
进行业务处理
-> 应用上下文出栈 (app_ctx
)
-> 请求上下文出栈 (request_ctx
)
-> 保存 session
或 sessino_id
进 cookie
-> 返回应答
-> 数据写入对应的文档描述符并刷新
其实 flask
的源码阅读起来并不吃力,看下来会发现 flask
框架代码的思路结构非常的清晰,并惊叹于这个框架的可扩展性,flask
的源码非常值得学习和借鉴。
参考文章:https://www.dazhuanlan.com/scientific/topics/950698