Skip to main content

集群缓存库

项目描述

依赖项:

  • numpy (用于 FilesCacheClient 中的优化)

  • python-memcached (用于 MemcachedClient)

  • pyzmq < 14 (用于一致性支持)

  • dnspython (用于 MemcachedClient 的基于 dns 的动态负载平衡)

  • cython (用于 InprocCacheClient 中的优化)

可选功能

在项目中声明依赖项时,可以引用以下可选功能:

  • mq (chorde.mq 下的所有内容,主要是拉 pyzmq)

  • shmem (共享内存工具,优化文件缓存客户端所需)

  • 内存缓存

  • 弹力痛

基本用法:

最简单的使用方法是创建一个受支持的缓存客户端,然后直接使用它,比如

from chorde.clients.inproc import InprocCacheClient
from chorde import CacheMissError
c = InprocCacheClient(200)
c.put(3, 10, 300) # put value 10 on key 3, TTL 5min
assert 10 == c.get(3)
try:
    c.get(5)
except CacheMissError:
    print "miss"

这将创建一个进程内 LRU 缓存。进程内部分表示它是进程私有的,不与其他进程共享。

LRU 有两种实现,具有不同的性能特征。InprocCacheClient可以替代存储实现作为参数,有关详细信息,请参阅模块。

默认的 LRUCache,可作为chorde.clients.inproc.Cache访问,是一个常规的 LRU,它使用优先级队列和串联的哈希表实现,因此它具有O(log n)写入和O(1)读取,但默认情况下所有读取需要写入(以更新 LRU)。这可以通过指定自定义选项来禁用,有关更多详细信息,请参阅模块的文档。

还有一个替代的近似 LRU,可在 chorde.clients.inproc.CuckooCache中访问,它实现了 cuckoo 哈希表的惰性版本,并具有O(1)读取和分期O(1)写入。它也比常规的 LRU 更节省空间,因此它更适合非常大的缓存,但它的驱逐策略将是近似的,因此不能保证总是驱逐实际最近最少使用的项目。

共享缓存

获取共享缓存最直接的方法是使用 memcache:

from chorde.clients.memcached import MemcachedClient
from chorde import CacheMissError
c = MemcachedClient(["localhost:11211"], checksum_key = "testing")
c.put(3, 10, 300)
assert 10 == c.get(3)
try:
    c.get(5)
except CacheMissError:
    print "miss"

MemcachedClient 的使用与任何其他客户端一样,只是在此示例中,它与本地 memcached 通信,侦听 localhost 端口 11211。可以提供多个客户端,并且密钥上的一致哈希将用于在它们之间分散负载.

如果给定一个主机名,并且该主机名指向多个IP地址,将获得相同的效果,并且将根据DNS条目上指定的TTL动态更新分配。例如,这使客户端与 Amazon ElastiCache 的“配置终端节点”无缝协作,这是一个指向缓存节点之一的 DNS 条目。但它只适用于单节点集群。对于多节点集群,请使用 chorde.clients.elasticache.ElastiCacheClient,它更进一步并查询此配置端点以获取所有其他节点。

请注意,针对 memcached 使用的键不是给定的键,因为客户端支持任何可散列对象作为键,而 memcached 仅支持字符串键的子集。MemcachedClient 在键格式和/或长度和值大小方面没有 memcached 的限制。它通过构造适用于 memcached 的派生键来解决 memcaches 的限制。但是,提供大量键或值会产生开销,因此通常建议避免使用它们。

MemcachedClient 中的值是 plickled、压缩和使用校验和密钥签名的,因此对于通过 pickle 的恶意代码注入和导致 cPickle 转储核心的传输错误(当输入未经检查的数据时确实会发生这种情况),它相对安全。

如果压缩成为瓶颈,这不应该是一个问题,除非它是具有很少可压缩值的高流量缓存,可以禁用它。查看 MemcachedClient 的文档以获取更多详细信息。

为了减少往返,所有客户端(特别是 MemcachedClients)都支持 getMulti 和 getTtlMulti,以一次获取多个键:

from chorde.clients.memcached import MemcachedClient
from chorde import CacheMissError
c = MemcachedClient(["localhost:11211"], checksum_key = "testing")
c.put(3, 10, 300)
c.put(4, 20, 300)
assert {3:10, 4:20, 5:None} == dict(c.getMulti([3,4,5], None))

有关更多详细信息,请参阅 clients.base 上的文档。

多级缓存

处理远程缓存时的一种常见方法,例如上面使用 memcached 的示例,至少具有两个级别:memcached 本身和进程内小型缓存,以避免一直与 memcached 通信。

这可以通过分层客户端直接完成:

from chorde.clients.memcached import MemcachedClient
from chorde.clients.inproc import InprocCacheClient
from chorde.clients.tiered import TieredInclusiveClient
from chorde import CacheMissError
l1 = InprocCacheClient(10)
l2 = MemcachedClient(["localhost:11211"], checksum_key="test")
c = TieredInclusiveClient(l1,l2)
c.put(3, 10, 300)
assert 10 == c.get(3)
try:
    c.get(5)
except CacheMissError:
    print "miss"

在这里,我们构建了一个包容的分层客户端,其中较高级别的元素通过复制而不是交换来提升到较低级别。这意味着它们之间存在重复,但这通常在这样的情况下是最好的,其中上层在进程之间共享。

目前不提供独占客户端,因为在这些类型的缓存上很少使用独占模式。

装饰器

考虑缓存的一种更自然的方式是,它是普通函数的装饰器。

与其显式地放入和从缓存中获取,人们可以简单地将缓存视为对其他昂贵功能的优化。

chorde.decorators 中的装饰器提供了大量的功能和灵活性,这些示例仅涵盖最基本的用法:

假设c是我们要用于缓存的客户端,

from chorde.decorators import cached
import random

@cached(c, ttl=300, async_ttl=-60)
def expensive_func(x):
    return x * random.random()

print expensive_func(1)
print expensive_func(1) # Should return the same
print expensive_func.async()(1) # will refresh asynchronously every minute
print expensive_func.future()(1).result() # same as before, but using the futures interface
print expensive_func.peek(1) # just check the cache
print expensive_func.put(1, _cache_put=5) # write an explicit value
print expensive_func.async().lazy(1) # don't wait, raise CacheMissError if not available, compute in background
print expensive_func.future().lazy(1).result() # same as before, but using the futures interface

在那里,async_ttl 表示触发异步重新计算的最小 TTL(您可以使用它来避免不得不等待重新计算)。负值使其相对于总 TTL,因此 -60 始终意味着每分钟(60 秒)重新计算一次。普通的 ttl 是一个绝对限制,不会返回比这更早的结果。

chorde.decorators.cached 上的文档将更多地说明调用缓存函数的方式。

一般来说,这些条款是:

  • 懒惰:不等待计算,返回缓存结果或引发 CacheMissError。当与 async 结合使用时,它将在后台计算。

  • 窥视:不要计算。类似于惰性,但它永远不会触发计算

  • 异步:昂贵的事情(计算)发生在后台线程池上。

  • 未来:返回期货而不是结果,使用未来在结果可用时得到通知。实际的缓存访问发生在线程池上。一种非阻塞的调用方式。

  • 刷新:立即重新计算值。

与其他库集成

装饰器的 future() 接口特别适合与其他可以与 futures 对话的库集成。然而,Chorde 的 future 并不直接与其他库兼容,但它们可以很容易地像这样包装:

import tornado.web
import tornado.gen
from chorde.clients.async import makeFutureWrapper

WF = makeFutureWrapper(tornado.web.Future)

...

@tornado.gen.coroutine
def get(self):
    some_result = yield WF(some_func.future()(some_args))

有更好的方法与 tornado >= 4.0 集成

from chorde.external_integration import monkey_patch_tornado
monkey_patch_tornado()

import tornado.web
import tornado.gen

...

@tornado.gen.coroutine
def get(self):
    some_result = yield some_func.future()(some_args)

附加文件

项目详情


下载文件

下载适用于您平台的文件。如果您不确定要选择哪个,请了解有关安装包的更多信息。

源分布

chorde-0.8.4.tar.gz (869.5 kB 查看哈希)

已上传 source