8

我怀疑这甚至是可能的,但这是问题和建议的解决方案(建议解决方案的可行性是这个问题的对象):


我有一些“全局数据”需要可用于所有请求。我将这些数据保存到 Riak 并使用 Redis 作为缓存层以提高访问速度(目前......)。数据被分成大约 30 个逻辑块,每个大约 8 KB。

每个请求都需要读取这些 8KB 块中的 4 个,从而从 Redis 或 Riak 读取 32KB 的数据。这是对任何需要读取的特定于请求的数据的补充(这是相当多的)。

假设每秒甚至 3000 个请求(这不是实时服务器,所以我没有实数,但 3000ps 是一个合理的假设,可能会更多),这意味着从 Redis 或 Riak 传输 96KBps 到已经不是- 从应用程序逻辑进行的其他无关紧要的调用。此外,Python每秒 解析 3000 次这些 8KB 对象的 JSON 。


所有这些——尤其是 Python 必须反复反序列化数据——似乎完全是一种浪费,一个完美优雅的解决方案是将反序列化的数据缓存在 Python 的内存本机对象中,我可以定期刷新它和当所有这些“静态”数据变得陈旧时。几分钟(或几小时)一次,而不是每秒 3000 次。

但我不知道这是否可能。您实际上需要一个“始终运行”的应用程序来缓存其内存中的任何数据。而且我知道在 nginx+uwsgi+python 组合中情况并非如此(相对于节点之类的东西)-据我所知, python 内存数据不会在所有请求中持久保存,除非我大错特错。

不幸的是,这是一个我“继承”的系统,因此无法在基础技术方面进行太多更改,我也不了解 nginx+uwsgi+python 组合在启动 Python 进程和持久化方面的工作原理Python 内存数据——这意味着我可能对上面的假设大错特错!


因此,关于该解决方案是否可行的直接建议+ 对可以帮助我了解 nginx+uwsgi+python 在启动新进程和内存分配方面如何工作的材料的参考将大有帮助。

PS:

  1. 已经阅读了 nginx、uwsgi 等的一些文档,但还没有完全理解我的用例的后果。希望现在能在这方面取得一些进展

  2. 如果内存中的事情可以解决,我会放弃 Redis,因为我只缓存我上面提到的静态数据。这使得进程内持久的内存中 Python 缓存对我来说更具吸引力,减少了系统中的一个移动部分,并且每个请求至少有四个网络往返。

4

4 回答 4

3

你的建议不是直接可行的。由于新进程可以在您的控制之外上下旋转,因此无法将原生 Python 数据保存在内存中。

但是,有几种方法可以解决这个问题。

通常,您只需要一级键值存储即可。有时,为值提供固定大小的缓冲区(您可以直接将其用作str//对象;您需要在其中进行任何其他操作bytes或以其他方式进行序列化)就是您所需要的。在这种情况下,uWSGI 的内置缓存框架将处理您需要的一切。bytearraystruct

如果您需要更精确的控制,您可以查看缓存是如何实现的,SharedArea并进行一些自定义。但是,我不建议这样做。它基本上为您提供了与文件相同的 API,与仅使用文件相比,唯一真正的优势是服务器将管理文件的生命周期;它适用于所有支持 uWSGI 的语言,即使是那些不允许文件的语言;如果您以后需要,它可以更轻松地将您的自定义缓存迁移到分布式(多计算机)缓存。我认为这些都与你无关。

另一种获得平面键值存储但没有固定大小缓冲区的方法是使用 Python 的 stdlib anydbm。键值查找与 Python 一样:它看起来dict像哈希表。

如果你需要处理一些其他简单的类型——任何可以非常快地 un/pickle 的类型,比如 s——int你可能需要考虑shelve.

如果您的结构足够严格,您可以将键值数据库用于顶层,但通过 a 访问值ctypes.Structure,或使用 反序列化/序列化struct。但通常,如果你能做到这一点,你也可以消除顶级,此时你的整个事情只是一个大StructureArray

此时,您可以只使用普通文件进行存储——要么mmap它(for ctypes),要么只是openread它(for struct)。

或者使用multiprocessing's Shared ctypesObjects直接访问你Structure的共享内存区域。

同时,如果您实际上并不总是需要所有的缓存数据,只是偶尔需要一些零碎的数据,这正是数据库的用途。同样,anydbm等可能是您所需要的,但如果您有复杂的结构,请绘制一个 ER 图,将其转换为一组表,然后使用 MySQL 之类的东西。

于 2013-03-15T23:57:14.597 回答
1

“据我所知,python 内存数据不会保留在所有请求中,除非我大错特错。”

你误会了。

在 CGI 机制上使用 uwsgi 的全部意义在于跨线程保存数据并节省每次调用的初始化开销。您必须processes = 1在文件中进行设置.ini,或者根据 uwsgi 的配置方式,它可能会代表您启动 1 个以上的工作进程。记录envand 查找'wsgi.multiprocess': Falseand 'wsgi.multithread': True,并且单个 worker 的所有uwsgi.core线程都应该显示相同的数据。

您还可以使用内置的stats-server.

这就是为什么 uwsgi 提供了多线程操作数据存储lockunlock功能。

您可以通过在您的应用程序中添加一个路由来轻松测试这一点,该/status路由仅转储您的全局数据对象的 json 表示,并在更新商店的操作后每隔一段时间查看它。

于 2017-07-28T23:13:27.467 回答
1

你没有说要写回这些数据,它是静态的吗?在这种情况下,解决方案非常简单,我不知道所有“它不可行”的响应是怎么回事。

Uwsgi 工作者始终运行的应用程序。所以数据绝对会在请求之间持久化。您需要做的就是将内容存储在全局变量中,就是这样。请记住,它是每个工人的,工人确实会不时重启,因此您需要适当的加载/失效策略。

如果数据很少更新(很少会在更新时重新启动服务器),您可以节省更多。只需在应用程序构建期间创建对象。这样,它们将被创建一次,然后所有的工作人员将分叉出主服务器,并重用相同的数据。当然,它是写时复制,所以如果你更新它,你将失去内存优势(如果 python 决定在 gc 运行期间压缩它的内存,也会发生同样的事情,所以它不是超级可预测的)。

于 2017-07-29T00:36:19.070 回答
0

我自己从来没有真正尝试过,但你可以使用 uWSGI 的SharedArea来完成你所追求的吗?

于 2013-03-15T23:40:58.890 回答