【问题标题】:What can go wrong if I use SimpleCache in my Flask app如果我在 Flask 应用程序中使用 SimpleCache 会出现什么问题
【发布时间】:2015-05-08 03:14:32
【问题描述】:

我们使用以下设置:NGINX+Gunicorn+Flask。我们只需要添加一点缓存,每个 Flask worker 不超过 5Mb。 SimpleCache 似乎是最简单的解决方案 - 它在 Python 进程本身内部使用本地内存。

不幸的是,文档声明如下:

"单进程环境的简单内存缓存。这个类 主要为开发服务器而存在,并不是 100% 线程安全的。”

但是,我看不出线程安全在我们的设置中的哪些方面很重要。我认为 Gunicorn 让几个 Flask 工作人员运行,每个工作人员都有自己的小缓存。什么可能出错?

【问题讨论】:

    标签: python python-2.7 caching flask gunicorn


    【解决方案1】:

    我目前正在处理用户登录应用程序后我想将他的 IP、用户名插入数据库的场景。

    现在,我只能这样做一次的方法是使用缓存将用户的 ip 和用户名存储在缓存中。

    现在,当每个 gunicorn 进程初始化自己的缓存时,问题就出现了。如果为 proc1 的缓存添加用户名 + ip 组合,如果 proc2 接收同一用户的下一个请求,它将不会在其缓存中找到它,因此将其再次添加到其缓存和数据库中,这是不合适的。因此,在这种情况下,线程安全(进程安全)缓存很重要。

    示例日志:

    2015-07-07 22:42:31 - myapp.views:29 - DEBUG - not from cache user1100.100.100.100, <type 'unicode'> [14776]
    2015-07-07 22:42:31 - myapp.views:30 - DEBUG - from cache : user1100.100.100.100, <type 'unicode'> [14776]
    2015-07-07 22:42:40 - myapp.views:29 - DEBUG - not from cache user1100.100.100.100, <type 'unicode'> [14776]
    2015-07-07 22:42:40 - myapp.views:30 - DEBUG - from cache : user1100.100.100.100, <type 'unicode'> [14776]
    2015-07-07 22:42:41 - myapp.views:29 - DEBUG - not from cache user1100.100.100.100, <type 'unicode'> [14779]
    2015-07-07 22:42:41 - myapp.views:30 - DEBUG - from cache : None, <type 'NoneType'> [14779]
    2015-07-07 22:42:41 - myapp.views:32 - DEBUG - new username ip [14779]
    2015-07-07 22:42:41 - myapp.views:38 - DEBUG - User : user1, ip : 100.100.100.100, noted at time : Tue Jul  7 22:42:41 2015, login_count : None [14779]
    

    您可以看到 gunicorn 进程 14776 首先将其添加到其缓存中,并且下一个请求被 14776 拾取,因此数据库条目仅发生一次,但之后下一个请求被 14779 拾取,并将其添加到其缓存中并因此分贝。

    cache = SimpleCache(threshold=1000, default_timeout=3600)
    

    使用基于 memcache 或 redis 的缓存可能会解决这个问题。我自己也在试验。

    【讨论】:

    • 把这个留在这里:SimpleCache 是一个dict“在内部”,但在将数据存储到缓存中之前会使用pickle 序列化数据,因此任何假设对象被简单存储的技巧(通过参考)进入字典将不起作用。
    【解决方案2】:

    对于您使用 gunicorn 的用例,不存在多线程问题,因为每个服务都在其自己的进程中单线程运行。但潜在的问题是“脏”读取数据。

    想想下面的案例:

    1. process1 从 db 读取并填充自己的缓存 cache1
    2. process2 使用相同的查询从同一个表中读取并填充它自己的缓存 cache2
    3. process2 使用新数据更新表并使旧缓存2 无效
    4. process1 再次执行相同的查询,从缓存1 中读取过期数据!。这是问题发生的时候,因为 process1/cache1 不知道数据库更新

    【讨论】:

    • 你找到办法了吗?
    猜你喜欢
    • 2020-01-29
    • 2020-08-10
    • 2012-01-17
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多