Skip to content

Instantly share code, notes, and snippets.

@siteshen
Created March 3, 2016 12:16
Show Gist options
  • Select an option

  • Save siteshen/6a4e4b76008d7e37aef4 to your computer and use it in GitHub Desktop.

Select an option

Save siteshen/6a4e4b76008d7e37aef4 to your computer and use it in GitHub Desktop.
A simple reads-write splitting redis client based on https://github.com/andymccurdy/redis-py.
import random
from redis import StrictRedis
from redis.connection import Connection, ConnectionPool
class RWSplitConnectionPool(object):
READ_COMMANDS = [
'info', 'smembers', 'hlen', 'hmget', 'srandmember', 'hvals', 'randomkey', 'strlen',
'dbsize', 'keys', 'ttl', 'lindex', 'type', 'llen', 'dump', 'scard', 'echo', 'lrange',
'zcount', 'exists', 'sdiff', 'zrange', 'mget', 'zrank', 'get', 'getbit', 'getrange',
'zrevrange', 'zrevrangebyscore', 'hexists', 'object', 'sinter', 'zrevrank', 'hget',
'zscore', 'hgetall', 'sismember',
]
def __init__(self, master_connection_pool=None, slave_connection_pools=None,
master_kwargs=None, slave_kwargs_list=None,
connection_class=Connection, max_connections=None):
if master_connection_pool is None:
master_connection_pool = ConnectionPool(
connection_class=connection_class, max_connections=max_connections, decode_responses=True, **master_kwargs)
if not master_connection_pool:
raise ValueError('Failed to create master_connection_pools')
if slave_connection_pools is None:
slave_connection_pools = [ConnectionPool(
connection_class=connection_class, max_connections=max_connections, decode_responses=True, **slave_kwargs)
for slave_kwargs in slave_kwargs_list]
if not slave_connection_pools:
raise ValueError('Failed to create slave_connection_pools')
self.master_connection_pool = master_connection_pool
self.slave_connection_pools = slave_connection_pools
@classmethod
def from_urls(cls, master_url, slave_urls, db=None, decode_components=False, **kwargs):
master_connection_pool = ConnectionPool.from_url(
master_url, db=db, decode_components=decode_components, **kwargs)
slave_connection_pools = [ConnectionPool.from_url(
slave_url, db=db, decode_components=decode_components, **kwargs) for slave_url in slave_urls]
return cls(master_connection_pool=master_connection_pool, slave_connection_pools=slave_connection_pools)
def get_connection(self, command_name, *keys, **options):
if command_name.lower() in self.READ_COMMANDS:
real_connection_pool = random.choice(self.slave_connection_pools)
else:
real_connection_pool = self.master_connection_pool
conn = real_connection_pool.get_connection(self, command_name, *keys, **options)
# HACK: self.release() need connection pool info, save it in connection_pool
setattr(conn, 'connection_pool', real_connection_pool)
return conn
def release(self, connection):
return connection.connection_pool.release(connection)
class RWSplitStrictRedis(StrictRedis):
'''
>>> redis_client = RWSplitStrictRedis({}, [{'port': 12306}])
>>> redis_client.set('doctest:0c99a5c1-c1fe-41d2-9972-ec21a396c531', 'true')
True
>>> redis_client.get('doctest:0c99a5c1-c1fe-41d2-9972-ec21a396c531')
Traceback (most recent call last):
...
redis.exceptions.ConnectionError: Error 61 connecting to localhost:12306. Connection refused.
'''
def __init__(self, master_kwargs=None, slave_kwargs_list=None, connection_pool=None, **kwargs):
if not connection_pool:
connection_pool = RWSplitConnectionPool(master_kwargs=master_kwargs, slave_kwargs_list=slave_kwargs_list)
super(RWSplitStrictRedis, self).__init__(connection_pool=connection_pool, **kwargs)
@classmethod
def from_urls(cls, master_url, slave_urls, db=None, **kwargs):
connection_pool = RWSplitConnectionPool.from_urls(master_url, slave_urls, db=db, **kwargs)
return cls(connection_pool=connection_pool, **kwargs)
if __name__ == '__main__':
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment