在 Python+uWSGI 应用中使用缓存

uWSGI 采用的是多进程模式。如果在 uWSGI 中运行的 Python 需要共享数据,可以使用第三方服务如 Redis/Memcached 等。如果数据量不大,还可以使用 uWSGI 提供的 缓存框架

uWSGI 有两套缓存框架。旧的一套叫做 WebCaching ,采用 cache 开头的配置项进行配置。我这里使用的是新的框架,采用 cache2 配置项进行配置。配置语法使用的是 key=value 语法。

 1[uwsgi]
 2
 3master = true
 4processes = 4
 5threads = 1
 6max-requests = 6000
 7chmod-socket = 664
 8buffer-size = 32768
 9thunder-lock = true
10cache2 = name=default,items=20,blocksize=65536,keysize=60,bitmap=1
11
12wsgi-file = manage.py
13callable = flaskapp
14uid = app
15gid = app
16
17venv = %dvenv
18pidfile = %d%n.pid
19http = 127.0.0.1:5001

在生产服务器中,开启 master 线程是必须的。uWSGI 在启动的时候会自动开启一个 sweeper 线程来处理过期。从下面的 log 可以看出这点:

1WSGI app 0 (mountpoint='') ready in 2 seconds on interpreter 0x7f95aa818e00 pid: 54383 (default app)
2*** uWSGI is running in multiple interpreter mode ***
3spawned uWSGI master process (pid: 54383)
4spawned uWSGI worker 1 (pid: 54401, cores: 1)
5spawned uWSGI worker 2 (pid: 54402, cores: 1)
6cache sweeper thread enabled
7spawned uWSGI http 1 (pid: 54403)

如果加入了 purge_lru 的配置,则上面那句 cache sweeper thread enabled 不会出现。

1cache2 = name=default,items=20,blocksize=65536,keysize=60,bitmap=1,purge_lru=0

在文档中并没有找到 purge_lru 设置为 0 是否可以禁用缓存过期这个功能。倒是老版本的缓存设置中有一个 cache-no-expire 选项可以直接禁用缓存过期功能。

uWSGI 会自动插入一个名为 uwsgi模块 到运行在其下的 Python app 中,使用这个模块提供的 缓存接口 可以对 uWSGI 提供的缓存功能进行操作,有这样几个接口:

  • uwsgi.cache_get
  • uwsgi.cache_set
  • uwsgi.cache_update
  • uwsgi.cache_exists
  • uwsgi.cache_del
  • uwsgi.cache_clear

文档写得非常简略,从字面也很容易理解它们的意思。

为了方便使用,我封装了一个 Cache 类。这样在本地开发的时候直接使用一个 dict 模仿缓存,而在 uWSGI 环境中则可以直接使用缓存功能。

 1import pickle
 2
 3class Cache(object):
 4    """
 5    处理缓存
 6    在 uWSGI 中运行的时候,使用 UWSGI 的缓存机制,实现进程间共享
 7    否则,缓存到一个 dict 中
 8    """
 9    def __init__(self):
10        self.__g = None
11        try:
12            self.__uwsgi = importlib.import_module('uwsgi')
13            print('USE CACHE UWSGI')
14        except:
15            self.__g = {}
16            print('USE CACHE MEMORY')
17
18    def _getuwsgicache(self, name):
19        """
20        获取 UWSGI 缓存
21        :param name: 真实的 name,带有 regional 信息
22        :return: 序列化之后的 python 对象
23        """
24        raw_value = self.__uwsgi.cache_get(name)
25        # print('_getuwsgicache:', raw_value)
26        if raw_value is not None:
27            return pickle.loads(raw_value, encoding='utf8')
28        return None
29
30    def _setuwsgicache(self, name, value):
31        """
32        设置 UWSGI 缓存
33        :param name: 设置名称
34        :param value: 值
35        :return:
36        """
37        if value is None:
38            self.__uwsgi.cache_del(name)
39            return
40        raw_value = pickle.dumps(value)
41        # print('_setuwsgicache:', raw_value)
42        if self.__uwsgi.cache_exists(name):
43            self.__uwsgi.cache_update(name, raw_value)
44        else:
45            self.__uwsgi.cache_set(name, raw_value)
46
47    def get(self, name):
48        if self.__g is None:
49            return self._getuwsgicache(name)
50        return self.__g.get(name, None)
51
52    def set(self, name, value):
53        if self.__g is None:
54            self._setuwsgicache(name, value)
55        else:
56            self.__g[name] = value

更一般的使用方式是在 uWSGI 配置中通过内部路由来使用缓存。这部分可以参考 The uWSGI Caching Cookbook

全文完