python-memcached内存缓存客户端的编写方式是每个线程都有自己的连接。这使得python-memcached代码变得简单,这很好,但是如果你的应用程序有数百或数千个线程(或者如果你运行大量应用程序),则会出现问题,因为你将很快用完memcache中的可用连接。
通常这种问题通过使用连接池来解决,实际上我见过的Java memcache库实现了连接池。在阅读了各种Python memcache库的文档后,似乎唯一一个提供连接池的是pylibmc,但它对我来说有两个问题:它不是纯Python,并且它似乎没有超时来保留一个来自游泳池的客户。虽然不是纯粹的Python可能不是一个交易破坏者,没有超时肯定是。还不清楚这些池如何使用例如dogpile.cache。
我希望找到一个带有连接池的纯Python memcache客户端,它可以与dogpile.cache一起使用,但我也对其他建议持开放态度。不过,我宁愿避免更改应用程序逻辑(比如将所有memcache操作推送到更少的后台线程中)。
答案 0 :(得分:4)
一位同事提出了一个似乎对我们的用例运行良好的想法,所以在这里分享。基本思想是创建预先使用的memcache客户端数量,将它们放入队列中,每当需要memcache客户端时,从队列中提取一个。由于Queue.Queue get()方法具有可选的超时参数,您还可以处理无法及时获取客户端的情况。您还需要在memcache客户端中处理threading.local的使用。
以下是它在代码中的工作原理(请注意,我实际上并没有运行这个确切的版本,因此可能存在一些问题,但这应该可以让您了解文本描述对您没有意义):< / p>
import Queue
import memcache
# See http://stackoverflow.com/questions/9539052/python-dynamically-changing-base-classes-at-runtime-how-to
# Don't inherit client from threading.local so that we can reuse clients in
# different threads
memcache.Client = type('Client', (object,), dict(memcache.Client.__dict__))
# Client.__init__ references local, so need to replace that, too
class Local(object): pass
memcache.local = Local
class PoolClient(object):
'''Pool of memcache clients that has the same API as memcache.Client'''
def __init__(self, pool_size, pool_timeout, *args, **kwargs):
self.pool_timeout = pool_timeout
self.queue = Queue.Queue()
for _i in range(pool_size):
self.queue.put(memcache.Client(*args, **kwargs))
def __getattr__(self, name):
return lambda *args, **kw: self._call_client_method(name, *args, **kw)
def _call_client_method(self, name, *args, **kwargs):
try:
client = self.queue.get(timeout=self.pool_timeout)
except Queue.Empty:
return
try:
return getattr(client, name)(*args, **kwargs)
finally:
self.queue.put(client)
答案 1 :(得分:0)
非常感谢@Heikki Toivenen为这个问题提供了想法!但是,我不确定如何准确调用get()方法以便在PoolClient中使用memcache客户端。直接调用任意名称的get()方法会产生AttributeError或MemcachedKeyNoneError。
通过结合@Heikki Toivonen和pylibmc解决问题的方法,我想出了以下代码来解决这个问题并在此处发布,以方便将来的用户(我有调试了这段代码,它应该可以运行了):
import Queue, memcache
from contextlib import contextmanager
memcache.Client = type('Client', (object,), dict(memcache.Client.__dict__))
# Client.__init__ references local, so need to replace that, too
class Local(object): pass
memcache.local = Local
class PoolClient(object):
'''Pool of memcache clients that has the same API as memcache.Client'''
def __init__(self, pool_size, pool_timeout, *args, **kwargs):
self.pool_timeout = pool_timeout
self.queue = Queue.Queue()
for _i in range(pool_size):
self.queue.put(memcache.Client(*args, **kwargs))
print "pool_size:", pool_size, ", Queue_size:", self.queue.qsize()
@contextmanager
def reserve( self ):
''' Reference: http://sendapatch.se/projects/pylibmc/pooling.html#pylibmc.ClientPool'''
client = self.queue.get(timeout=self.pool_timeout)
try:
yield client
finally:
self.queue.put( client )
print "Queue_size:", self.queue.qsize()
# Intanlise an instance of PoolClient
mc_client_pool = PoolClient( 5, 0, ['127.0.0.1:11211'] )
# Use a memcache client from the pool of memcache client in your apps
with mc_client_pool.reserve() as mc_client:
#do your work here