Archive

Posts Tagged ‘memcached’

python在生产环境可选的memcached lib

May 15th, 2009 Sparkle 7 comments

memcached对动态语言帮助很大,因为动态语言的模式和实际的部署模型,python/php/ruby等不可能像Java那样在JVM内存中开辟一块内存来缓冲数据(而实际上Java这样做也会遇到容量不够,GC,集群等问题)。不过在实际使用中,我发现python memcached lib并没有想象中那么成熟、完整,经过一番研究之后,目前至少能勉强在生产环境中使用。

从memcached官方wiki中可以看到,python还是有不少可以使用的lib的,但是经过测试和比较之后,发现选择的方案也不多。我们还是从比较出名的web框架入手吧,毫无疑问,Django是目前python最好的框架之一,在生产环境使用它的人也比较多,而且它的文档很详细,有一节就是专门提到cache的部分 。Django推荐使用cmemcache,在不能使用cmemcache的情况(例如windows机器),就使用python-memcached。经过我的实际测试,这个或许不是性能最好,版本最新,功能最多的选择,但是是一个很稳靠的选择。

cmemcache封装的是libmemcache,这个类库很久没有更新了,目前libmemcached比较活跃,功能也比较全面。这里值得一提的是Python libmemcached是来自豆瓣贡献的类库,不过因为使用的人不多,所以最终我没有选择。另外还有一个pylibmc也是对libmemcached的封装,这是cmemcache_hash的作者写的类库(稍后会提到cmemcache_hash),很可惜我一直没有编译成功。

但是实际使用cmemcache/python-memcached的组合也是要进行一些封装的,首先有cmemcache不是线程安全的问题 ,另外就是Python libmemcached的hash算法有问题。什么?你从来不用超过1个的memcached服务器?我还是建议你至少用2个memcached服务器吧,至少有backup。

而cmemcache和Python libmemcached的hash算法不一样。这里我们还需要cmemcache_hash,名字可能有少少误导,它的作用是领到Python libmemcached的hash跟cmemcache一致

于是我们的封装代码大概是这样

try:
    import cmemcache as memcache
    logger.warn('load cmemcache')
    # although cmemcache might be thread safe
    # but we still make it thread local
    # also it can use separate socket connect in each thread for more performance
    from threading import local
except ImportError, e:
    logger.warn('cmemcache error: %s', e)
    import memcache
    import cmemcache_hash
    logger.warn('load python-memcache with cmemcache_hash')
    # python-memcache is already using thread local
    class local():
        pass
 
class Memcache(local):
    def __init__(self, servers):
        self._current = memcache.Client(servers)
    def current(self):
        return self._current
Categories: Uncategorized Tags: ,

python cmemcache是不是线程安全的

February 14th, 2009 Sparkle 6 comments

答案是,我不确定,但是我更倾向于认为它是线程不安全的

原因在于
1、网上任何讨论,都从未提及过cmemcache所用的libmemcache是线程安全的,也没有提及过cmemcache实现了线程安全,而libmemcached从一开始就强调自己是线程安全的
2、我个人不太熟悉C代码,所以看libmemcache的代码也没有办法判断出来,但是我看到cmemcache的c接口代码在针对socket操作的代码前后使用了Py_BEGIN_ALLOW_THREADS;Py_END_ALLOW_THREADS;进行包围,而这两个宏的作用,是解除GIL,让多线程可以进入相应的代码。天啊,本来我们还期望对socket操作进行加锁,现在竟然还竟然解除GIL。本来因为GIL的存在,可能还有些希望,现在cmemcache为了让socket操作不要阻塞,反而令到自己是线程不安全的。(如有理解错误请指正)
3、python一直习惯的部署模型是单线程多进程,所以线程安全对很多类库来说,并不重要也不考虑。但是现在越来越多的python程序部署使用了多线程的模型(又或者多线程多进程),线程安全问题也凸显出来

综上所属,我的结论是,我不确定是否线程安全的,呵呵,但是我认为要进行线程安全的修正,至少没有坏处

修正方法很简单,直接套用python-memcached的做法(至于这种做法的优劣也在那篇文章有提及到)

import cmemcache as memcache
from threading import local
class Memcache(local):
    def __init__(self, servers):
        self._current = memcache.Client(servers)
    def current(self):
        return self._current
Categories: Uncategorized Tags: ,

python-memcached是不是线程安全的

January 22nd, 2009 Sparkle No comments

答案是肯定的,前提你在使用Python 2.4+和python-memcached 1.36+
为什么我们需要线程安全的memcached client,因为我们的实际应用一般是多线程的模型,例如cherrypy、twisted,如果python-memcached不是线程安全的话,引起的问题不仅仅是并发修改共享变量这么简单,是外部socket链接的数据流的混乱
python-memcached怎么实现线程安全的呢?查看源代码看到

try:
    # Only exists in Python 2.4+
    from threading import local
except ImportError:
    # TODO:  add the pure-python local implementation
    class local(object):
        pass
 
 
class Client(local):

很取巧的让Client类继承threading.local,也就是Client里面的每一个属性都是跟当前线程绑定的。实现虽然不太优雅,但是很实在
但是别以为这样就可以随便在线程里面用python-memcached了,因为这种thread local的做法,你的应用必须要使用thread pool的模式,而不能不停创建销毁thread,因为每一个新线程的创建,对于就会使用一个全新的Client,也就是一个全新的socket链接,如果不停打开创建销毁thread的话,就会导致不停的创建销毁socket链接,导致性能大量下降。幸好,无论是cherrypy还是twisted,都是使用了thread pool的模式

参考资料:
http://code.djangoproject.com/ticket/3701
http://lists.danga.com/pipermail/memcached/2007-June/004482.html

Categories: Uncategorized Tags: , ,