Simple Life

和这个世界交手这许多年   你是否光彩依旧,兴致盎然...

您现在的位置是:首页 爱折腾 爱折腾详情

python中使用redis-py连接Redis

发布时间:2015-6-23 作者:Felix 浏览(3516)

        熟悉了redis数据库之后,自然会想到如何在python中连接redis,python中可以使用redis-py这个库,它实现了redis的大部分命令,并且新的版本中加入了线程池的功能,具体的下边来看了,我也是零零碎碎的看了,边写边整理帮助自己记忆,方便以后查询。

        安装:虽然称redis的python客户端称为redis-py,实际安装时的名字是redis.,执行 sudo pip install redis

        github地址: https://github.com/andymccurdy/redis-py

        运行需要先安装和运行起来redis-server的,具体看之前的 Redis初探 (http://felixonly.com/blog/detail/7/

        简单入门:

>>> import redis
>>> r = redis.StrictRedis(host='localhost', port='6379')
>>> r.set('name', 'felix')
True
>>> r.rpush('tmplist', 'welcome')
1L
>>> r.rpush('tmplist', 'to')
2L
>>> r.get('name')
'felix'
>>> r.lrange('tmplist', 0, -1)
['welcome', 'to']

         redis-py提供两个类Redis和StrictRedis用于实现Redis的命令,StrictRedis用于实现大部分官方的命令,并使用官方的语法和命令。具体的可以查看官方的Redis指令文档 (http://redis.io/commands),另外,In addition to the changes above, the Redis class, a subclass of StrictRedis, overrides several other commands to provide backwards compatibility with older versions of redis-py, 意思说Redis是StrictRedis的子类,是用于向后兼容旧版本的redis-py,来晚了。

        一步步来,看一下redis-py的源码实现,执行命令式调用的是 redis/client.py  下 StrictRedis的

# COMMAND EXECUTION AND PROTOCOL PARSING
def execute_command(self, *args, **options):
    "Execute a command and return a parsed response"
    pool = self.connection_pool  # 得到连接池
    command_name = args[0]
    connection = pool.get_connection(command_name, **options)  # 去线程池获取连接
    try:
        connection.send_command(*args)  # 命令执行,这里为Connection.send_command
        return self.parse_response(connection, command_name, **options)
    except (ConnectionError, TimeoutError) as e:
        connection.disconnect()
        if not connection.retry_on_timeout and isinstance(e, TimeoutError):
            raise
        connection.send_command(*args)
        return self.parse_response(connection, command_name, **options)
    finally:
        pool.release(connection)  # 释放连接

        连接池 你发现execute_command中有个获取连接池,往上看,connection_pool出自StrictRedis的__init__文件

def __init__(self, host='localhost', port=6379,
             db=0, password=None, socket_timeout=None,
             socket_connect_timeout=None,
             socket_keepalive=None, socket_keepalive_options=None,
             connection_pool=None, unix_socket_path=None,
             encoding='utf-8', encoding_errors='strict',
             charset=None, errors=None,
             decode_responses=False, retry_on_timeout=False,
             ssl=False, ssl_keyfile=None, ssl_certfile=None,
             ssl_cert_reqs=None, ssl_ca_certs=None):
    if not connection_pool:         
        *省略········*
        connection_pool = ConnectionPool(**kwargs)  # 创建连接池
    self.connection_pool = connection_pool 
    self._use_lua_lock = None
    
    self.response_callbacks = self.__class__.RESPONSE_CALLBACKS.copy()

        说明每次创建一个redis实例都会创建一个connection poll,redis-py使用connection pool来管理对一个redis server的所有连接,避免每次建立、释放连接的开销。看下connection pool的构造:

  class ConnectionPool(object):  
       ...........
    def __init__(self, connection_class=Connection, max_connections=None,
                 **connection_kwargs):   #类初始化时调用构造函数
        max_connections = max_connections or 2 ** 31  # 默认创建 2**31 个连接
        if not isinstance(max_connections, (int, long)) or max_connections < 0:  #判断输入的max_connections是否合法
            raise ValueError('"max_connections" must be a positive integer')
        self.connection_class = connection_class  #设置对应的参数
        self.connection_kwargs = connection_kwargs
        self.max_connections = max_connections
        self.reset()  #初始化ConnectionPool 时的reset操作
    def reset(self):
        self.pid = os.getpid()
        self._created_connections = 0  #已经创建的连接的计数器
        self._available_connections = []   #声明一个空的数组,用来存放可用的连接
        self._in_use_connections = set()  #声明一个空的集合,用来存放已经在用的连接
        self._check_lock = threading.Lock()
.......
    def get_connection(self, command_name, *keys, **options):  #在连接池中获取连接的方法
        "Get a connection from the pool"
        self._checkpid()
        try:
            connection = self._available_connections.pop()  #获取并删除代表连接的元素,在第一次获取connectiong时,因为_available_connections是一个空的数组,
            会直接调用make_connection方法
        except IndexError:
            connection = self.make_connection()
        self._in_use_connections.add(connection)   #向代表正在使用的连接的集合中添加元素
        return connection   
    def make_connection(self): #在_available_connections数组为空时获取连接调用的方法
        "Create a new connection"
        if self._created_connections >= self.max_connections:   #判断创建的连接是否已经达到最大限制,max_connections可以通过参数初始化
            raise ConnectionError("Too many connections")
        self._created_connections += 1   #把代表已经创建的连接的数值+1
        return self.connection_class(**self.connection_kwargs)     #返回有效的连接,默认为Connection(**self.connection_kwargs)
    def release(self, connection):  #释放连接,链接并没有断开,只是存在链接池中
        "Releases the connection back to the pool"
        self._checkpid()
        if connection.pid != self.pid:
            return
        self._in_use_connections.remove(connection)   #从集合中删除元素
        self._available_connections.append(connection) #并添加到_available_connections 的数组中
    def disconnect(self): #断开所有连接池中的链接
        "Disconnects all connections in the pool"
        all_conns = chain(self._available_connections,
                          self._in_use_connections)
        for connection in all_conns:
            connection.disconnect()

你也可以重写,默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数Redis,这样就可以实现多个Redis实例共享一个连接池。

>>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
>>> r = redis.Redis(connection_pool=pool)

         解析 Parser可以控制如何解析redis响应的内容。redis-py包含两个Parser类,PythonParser和HiredisParser。默认,如果已经安装了hiredis模块,redis-py会使用HiredisParser,否则会使用PythonParser。文档中说HiredisParser是C编写的,性能要比PythonParser提高10倍以上(Hiredis is a C library maintained by the core Redis team. Pieter Noordhuis was kind enough to create Python bindings. Using Hiredis can provide up to a 10x speed improvement in parsing responses from the Redis server. )所以肯定用这个啦,安装:

sudo pip install hiredis

     redis pipeline机制,可以在一次请求中执行多个命令,这样避免了多次的往返延时。

>>> pipe = r.pipeline()
>>> pipe.set('sex', 'man')
StrictPipeline<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>
>>> pipe.set('age', '25')
StrictPipeline<ConnectionPool<Connection<host=localhost,port=6379,db=0>>>
>>> pipe.execute()
[True, True]
>>> r.get('age')
'25'
>>> r.get('sex')
'man'
>>> pipe.set('fruit', 'apple').lpush('mylist', 'monday').lpush('mylist', 'tuesday').execute()
[True, 1L, 2L]
>>> r.get('fruit')
'apple'
>>> r.lrange('mylist', 0, -1)
['tuesday', 'monday']

redis-py默认在一次pipeline中的操作是原子的,但是可以修改:

pipe = r.pipeline(transaction=False)


基于 Django 搭建

服务器采用的 阿里云

域名来自 万网

苏ICP备16015443号

©2015-2016 felixglow.com.

GitHub

Design by Felix