flask分布式部署session的保存方案

2021-08-19  本文已影响0人  忘了呼吸的那只猫

flask 作为 web 应用框架若多机部署,第一个问题是需要一个请求接入网关,通常我们使用 nginx 统一进行流量的分发。
但随之而来会有一个新的问题,即 flasksession 多机之间会共享吗?带着这个问题,我们看看 flask 关于 session 的源码:

class (CallbackDict, SessionMixin):
  ...
class SecureCookieSessionInterface(SessionInterface):
    def open_session(self, app, request):
        ...
    def save_session(self, app, session, response):
        ...

以上是 flask.sessions.py 实现的主要框架:

SecureCookieSessionflasksession 类,可以简单的理解成一个 dict 对象。
SecureCookieSessionInterfaceflasksession 接口类,open_session 方法用于创建 sessionsave_session 方法用于将 session 加密并存放在 responsecookie 中。所以 flask 是默认将用户的 session 存储在客户端的 cookie 中,这样请求 - 应答的数据中就有了用户操作的上下文了,至于这么做的优劣将在下文分析。

1.服务器间 session 复制

session复制是早期的企业级的使用比较多的一种服务器集群 session 管理机制。应用服务器开启 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 进行统一的存储,所有服务器共享该存储服务上的数据
优点:服务水平扩展性良好,服务端存储,安全
缺点:每次请求至少需要一次内部网络请求,占用网络带宽
需要侵入业务代码

通过比较不难发现,服务端 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_sessionsave_session,将 session(dict)存储在 redis 并将 session_id(key)返回给客户端

客户端 http 请求
-> 服务端负载均衡至随机服务器
-> 应用上下文入栈 (app_ctx)
-> 请求上下文入栈 (request_ctx),同时生成 session
-> 通过 request_ctx 中的路由信息找到视图函数 (view_func)
-> view_func 进行业务处理
-> 应用上下文出栈 (app_ctx)
-> 请求上下文出栈 (request_ctx)
-> 保存 sessionsessino_idcookie
-> 返回应答
-> 数据写入对应的文档描述符并刷新
其实 flask 的源码阅读起来并不吃力,看下来会发现 flask 框架代码的思路结构非常的清晰,并惊叹于这个框架的可扩展性,flask 的源码非常值得学习和借鉴。
参考文章:https://www.dazhuanlan.com/scientific/topics/950698

上一篇下一篇

猜你喜欢

热点阅读