python-memcached并发调用get/set时出现RunTimeError:Second simultaneous read on fileno 8 detected.

出现异常如下 RuntimeError: Second simultaneous read on fileno 8 detected.  Unless you really know what you're doing, make sure that only one greenthread can read any particular socket.  Consider using a pools.Pool. If you do know what you're doing and want to disable this error, call eventlet.debug.hub_multiple_reader_prevention(False)

2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/lib/python2.7/dist-packages/memcache.py", line 862, in get
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     return self._get('get', key)
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/lib/python2.7/dist-packages/memcache.py", line 846, in _get
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     return _unsafe_get()
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/lib/python2.7/dist-packages/memcache.py", line 830, in _unsafe_get
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     rkey, flags, rlen, = self._expectvalue(server)
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/lib/python2.7/dist-packages/memcache.py", line 955, in _expectvalue
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     line = server.readline()
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/lib/python2.7/dist-packages/memcache.py", line 1125, in readline
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     data = recv(4096)
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/local/lib/python2.7/dist-packages/eventlet/greenio.py", line 249, in recv
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     timeout_exc=socket.timeout("timed out"))
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/local/lib/python2.7/dist-packages/eventlet/hubs/__init__.py", line 117, in trampoline
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     listener = hub.add(hub.READ, fileno, current.switch)
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/local/lib/python2.7/dist-packages/eventlet/hubs/poll.py", line 27, in add
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     listener = super(Hub, self).add(evtype, fileno, cb)
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake   File "/usr/local/lib/python2.7/dist-packages/eventlet/hubs/hub.py", line 126, in add
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake     evtype, fileno, evtype))
2013-03-08 18:27:27 17870 TRACE glance.store.chunkcache.fake RuntimeError: Second simultaneous read on fileno 8 detected.  Unless you really know what you're doing, make sure that only one greenthread can read any particular socket.  Consider using a pools.Pool. If you do know what you're doing and want to disable this error, call eventlet.debug.hub_multiple_reader_prevention(False)


除了这个之外再还有一堆的memcache.set返回0的情况。

出现问题时的大致模型如下

class CacheBackend(CacheBackendBase):
    '''
    Cache in memcached servers.
    '''

    def __init__(self):
        value_length = CONF.chunkcache_memcached_value_length
        servers = CONF.chunkcache_memcached_servers
        self.memcache = memcache.Client(servers, debug=1,
                                        server_max_value_length=value_length)

    @staticmethod
    def instance():
        global CACHE_BACKEND
        if CACHE_BACKEND is None:
            CACHE_BACKEND = CacheBackend()
        return CACHE_BACKEND

    def get(self, key, default=None):
        result = self.memcache.get(str(key))
        return result or default

    def exist(self, key):
        return self.get(str(key)) != None

    def set(self, key, value):
        result = self.memcache.set(str(key), value)
        if result == 0:
            raise exception.MemcacheSetError()
        return result

    def delete(self, key):
        if self.exist(key):
            result = self.memcache.delete(str(key))
            if result == 0:
                raise exception.MemcacheDeleteError()
        return True

    def clear(self):
        # No need for clearing memcached.
        return True

经过同事交流,推测可能是因为fork之前有socket连接在或者是多个进程重用了一个socket导致,但是感觉都不太能讲得通,对这块确实不太了解,先记录下来解决方案,有谁了解的求解释~


下面是解决的模型,大致改动就是在每个set,get,delete处都new一个memcache client,

可以新建一个socket?不过看memcache模块,好像也是有socket缓存的啊,会去缓存中拿同个socket,

下面的disconnect_all加与不加都一样,不会出现问题,而且在上面的模块中加上disconnect_all再重试也不起作用,

实在无法理解,但是确实解决了问题,多次测试都未复现上述问题。


class CacheBackend(CacheBackendBase):
    '''
    Cache in memcached servers.
    '''

    def __init__(self):
        self.value_length = CONF.chunkcache_memcached_value_length
        self.servers = CONF.chunkcache_memcached_servers

    @staticmethod
    def instance():
        global CACHE_BACKEND
        if CACHE_BACKEND is None:
            CACHE_BACKEND = CacheBackend()
        return CACHE_BACKEND

    def get(self, key, default=None):
        client = _connect(self.servers, self.value_length)
        result = client.get(str(key))
        client.disconnect_all()
        return result or default

    def exist(self, key):
        return self.get(str(key)) != None

    def set(self, key, value):
        client = _connect(self.servers, self.value_length)
        result = client.set(str(key), value)
        client.disconnect_all()
        if result == 0:
            raise exception.MemcacheSetError()
        return result

    def delete(self, key):
        client = _connect(self.servers, self.value_length)
        if self.exist(key):
            result = client.delete(str(key))
            if result == 0:
                raise exception.MemcacheDeleteError()
        client.disconnect_all()
        return True

    def clear(self):
        # No need for clearing memcached.
        return True


def _connect(servers, value_length=chunkstore.Store.CHUNKSIZE):
        return memcache.Client(servers, debug=1,
                               server_max_value_length=value_length)

另外想解释下,并不是单例或者维护一个client对象导致,去掉单例,问题一样,而且并发是多进程并发的,也不应该存在什么单例之类的问题。

你可能感兴趣的:(python,memcached)