#!/usr/bin/env python#encoding: utf-8
importasyncioimportdatetimeimportcollectionsimporttime as mod_timefrom asyncio importFuturefrom collections importnamedtuple, dequefrom tornado.tcpclient importTCPClientfrom tornado.escape importto_unicode, to_basestringclassRedisError(Exception):pass
classConnectionError(RedisError):pass
classRequestError(RedisError):def __init__(self, message, cmd_line=None):
self.message=message
self.cmd_line=cmd_linedef __repr__(self):ifself.cmd_line:return 'RequestError (on %s [%s, %s]): %s'\%(self.cmd_line.cmd, self.cmd_line.args,
self.cmd_line.kwargs, self.message)return 'RequestError: %s' %self.message__str__ = __repr__Message= namedtuple('Message', ('kind', 'channel', 'body', 'pattern'))
GeoData= namedtuple('GeoData', ('member', 'dist', 'coords', 'hash'))defstring_keys_to_dict(key_string, callback):return dict([(key, callback) for key inkey_string.split()])def dict_merge(*dicts):
merged={}for d indicts:
merged.update(d)returnmergeddef reply_to_bool(r, *args, **kwargs):returnbool(r)defmake_reply_assert_msg(msg):def reply_assert_msg(r, *args, **kwargs):return r ==msgreturnreply_assert_msgdef reply_set(r, *args, **kwargs):returnset(r)def reply_dict_from_pairs(r, *args, **kwargs):return dict(zip(r[::2], r[1::2]))def reply_str(r, *args, **kwargs):return r or ''
def reply_int(r, *args, **kwargs):return int(r) if r is not None elseNonedef reply_number(r, *args, **kwargs):if r is notNone:
num=float(r)if notnum.is_integer():returnnumelse:returnint(num)returnNonedef reply_datetime(r, *args, **kwargs):returndatetime.datetime.fromtimestamp(int(r))def reply_pubsub_message(r, *args, **kwargs):"""Handles a Pub/Sub message and packs its data into a Message object."""
if len(r) == 3:
(kind, channel, body)=r
pattern=channelelif len(r) == 4:
(kind, pattern, channel, body)=relif len(r) == 2:
(kind, channel)=r
body= pattern =Noneelse:raise ValueError('Invalid number of arguments')returnMessage(kind, channel, body, pattern)def reply_zset(r, *args, **kwargs):if r and 'WITHSCORES' inargs:return reply_zset_withscores(r, *args, **kwargs)else:returnrdef reply_zset_withscores(r, *args, **kwargs):return list(zip(r[::2], list(map(reply_number, r[1::2]))))def reply_hmget(r, key, *fields, **kwargs):returndict(list(zip(fields, r)))def reply_info(response, *args):
info={}defget_value(value):#Does this string contain subvalues?
if (',' not in value) or ('=' not invalue):returnvalue
sub_dict={}for item in value.split(','):
k, v= item.split('=')try:
sub_dict[k]=int(v)exceptValueError:
sub_dict[k]=vreturnsub_dictfor line inresponse.splitlines():
line=line.strip()if line and not line.startswith('#'):
key, value= line.split(':')try:
info[key]=int(value)exceptValueError:
info[key]=get_value(value)returninfodef reply_ttl(r, *args, **kwargs):return r != -1 and r orNonedef reply_map(*funcs):def reply_fn(r, *args, **kwargs):if len(funcs) !=len(r):raise ValueError('more results than functions to map')return [f(part) for f, part inzip(funcs, r)]returnreply_fndef reply_coords(r, *args, **kwargs):return [(float(c[0]), float(c[1])) for c inr]def reply_geo_radius(r, *args, **kwargs):
geo_data=[]for member inr:
name=member[0]
dist= coords = hs =Noneif 'WITHDIST' inargs:
dist= float(member[1])if 'WITHHASH' in args and 'WITHDIST' inargs:
hs= int(member[2])elif 'WITHHASH' inargs:
hs= int(member[1])if 'WITHCOORD' in args and 'WITHHASH' in args and 'WITHDIST' inargs:
coords= (float(member[3][0]), float(member[3][1]))elif 'WITHCOORD' in args and ('WITHHASH' in args or 'WITHDIST' inargs):
coords= (float(member[2][0]), float(member[2][1]))elif 'WITHCOORD' inargs:
coords= (float(member[1][0]), float(member[1][1]))
geo_data.append(GeoData(name, dist, coords, hs))returngeo_datadefto_list(source):ifisinstance(source, str):return[source]else:returnlist(source)
PUB_SUB_COMMANDS=('SUBSCRIBE','PSUBSCRIBE','UNSUBSCRIBE','PUNSUBSCRIBE',#Not a command at all
'LISTEN',
)
REPLY_MAP=dict_merge(
string_keys_to_dict('AUTH BGREWRITEAOF BGSAVE DEL EXISTS'
'EXPIRE HDEL HEXISTS'
'HMSET MOVE PERSIST RENAMENX SISMEMBER SMOVE'
'SETEX SAVE SETNX MSET',
reply_to_bool),
string_keys_to_dict('BITCOUNT DECRBY GETBIT HLEN INCRBY LINSERT'
'LPUSHX RPUSHX SADD SCARD SDIFFSTORE SETBIT SETRANGE'
'SINTERSTORE STRLEN SUNIONSTORE SETRANGE',
reply_int),
string_keys_to_dict('FLUSHALL FLUSHDB SELECT SET SETEX'
'SHUTDOWN RENAME RENAMENX WATCH UNWATCH',
make_reply_assert_msg('OK')),
string_keys_to_dict('SMEMBERS SINTER SUNION SDIFF', reply_set),
string_keys_to_dict('HGETALL BRPOP BLPOP', reply_dict_from_pairs),
string_keys_to_dict('HGET', reply_str),
string_keys_to_dict('SUBSCRIBE UNSUBSCRIBE LISTEN'
'PSUBSCRIBE UNSUBSCRIBE',
reply_pubsub_message),
string_keys_to_dict('ZRANK ZREVRANK', reply_int),
string_keys_to_dict('ZCOUNT ZCARD', reply_int),
string_keys_to_dict('ZRANGE ZRANGEBYSCORE ZREVRANGE'
'ZREVRANGEBYSCORE',
reply_zset),
string_keys_to_dict('ZSCORE ZINCRBY', reply_number),
string_keys_to_dict('SCAN HSCAN SSCAN', reply_map(reply_int, reply_set)),
string_keys_to_dict('GEODIST', reply_number),
string_keys_to_dict('GEOPOS', reply_coords),
string_keys_to_dict('GEORADIUS GEORADIUSBYMEMBER', reply_geo_radius),
{'HMGET': reply_hmget,'PING': make_reply_assert_msg('PONG'),'LASTSAVE': reply_datetime,'TTL': reply_ttl,'INFO': reply_info,'MULTI_PART': make_reply_assert_msg('QUEUED'),'TIME': lambda x: (int(x[0]), int(x[1])),'ZSCAN': reply_map(reply_int, reply_zset_withscores)}
)classResponseError(RedisError):def __init__(self, message, cmd_line=None):
self.message=message
self.cmd_line=cmd_linedef __repr__(self):ifself.cmd_line:return 'ResponseError (on %s [%s, %s]): %s'\%(self.cmd_line.cmd, self.cmd_line.args,
self.cmd_line.kwargs, self.message)return 'ResponseError: %s' %self.message__str__ = __repr__
classInvalidResponse(RedisError):pass
classLockError(RedisError):"Errors thrown from the Lock"
pass
classCmdLine(object):def __init__(self, cmd, *args, **kwargs):
self.cmd=cmd
self.args=args
self.kwargs=kwargsdef __repr__(self):return self.cmd + '(' + str(self.args) + ',' + str(self.kwargs) + ')'
classTornadoRedis:def __init__(
self,
host='localhost',
port=6379,
password=None,
selected_db=None,
max_connections=None
):
self.host=host
self.port=port
self.password=password
self.selected_db=selected_db#连接池
self.max_connections = max_connections or 2048self._created_connections= 0 #已创建的连接数
self._available_connections = set() #可以使用的连接
self._used_connections = set() #正在使用中的连接
self._waiting_connections = set() #等待使用的连接
def __del__(self):"""Python 垃圾回收机制
Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,
Python 会自动保证该对象引用计数为 1;当程序中有两个变量引用该 Python 对象时,Python 会自动保证该
对象引用计数为 2,依此类推,如果一个对象的引用计数变成了 0,则说明程序中不再有变量引用该对象,
表明程序不再需要该对象,因此 Python 就会回收该对象。"""
for conn inself._available_connections.union(self._used_connections):
conn.disconnect()for conn inself._waiting_connections:
conn.cancel()#Cancel the future and schedule callbacks.
asyncdefget_connection(self):"""Returns a pooled Redis server connection"""
try:
connection=self._available_connections.pop()exceptKeyError:
connection=self.make_connection()ifisinstance(connection, Future):
connection=await connectionifconnection:
await connection.connect()
self._used_connections.add(connection)returnconnectiondefmake_connection(self):"""Creates a new connection to Redis server"""
if self._created_connections >=self.max_connections:
f=Future()
self._waiting_connections.add(f)returnf
self._created_connections+= 1
return Connection(self.host, self.port, selected_db=self.selected_db, password=self.password)defrelease(self, connection):"""Releases the connection back to the pool"""
ifself._waiting_connections:
waiting=self._waiting_connections.pop()
waiting.set_result(connection)else:try:
self._used_connections.remove(connection)except(KeyError, ValueError):passself._available_connections.add(connection)classConnection(object):def __init__(
self,
host='localhost',
port=6379,
selected_db=None,
password=None,
):
self.host=host
self.port=port
self.subscribed=set()
self.subscribe_callbacks=deque()
self.unsubscribe_callbacks=[]
self.password=password
self.selected_db= selected_db or0
self._stream=None
self.client=TCPClient()
self.stream=None
self.info= {'db': 0, 'pass': None}def __del__(self):
self.disconnect()
asyncdefconnect(self):if notself.stream:
self.stream= await self.client.connect(host=self.host, port=self.port)
asyncdef _consume_bulk(self, tail, callback=None):
response= await self.stream.read_until(b'\r\n')#response = await self.stream.read_bytes(int(tail) + 2,partial=True)
ifisinstance(response, Exception):raiseresponseif notresponse:raise ResponseError('EmptyResponse')else:
response=to_unicode(response)
response= response[:-2]returnresponse
asyncdefconsume_multibulk(self, length, cmd_line):
tokens=[]while len(tokens)
data= await self.stream.read_until(b'\r\n')if notdata:raise ResponseError('Not enough data in response to %s, accumulated tokens: %s' %(cmd_line, tokens), cmd_line)
token=await self.process_data(data, cmd_line)
tokens.append(token)returntokens
asyncdefprocess_data(self, data, cmd_line):
data=to_basestring(data)
data= data[:-2] #strip \r\n
if data == '$-1':
response=Noneelif data == '*0' or data == '*-1':
response=[]else:
head, tail= data[0], data[1:]if head == '*':
response=await self.consume_multibulk(int(tail), cmd_line)elif head == '$':
response=await self._consume_bulk(tail)elif head == '+':
response=tailelif head == ':':
response=int(tail)elif head == '-':if tail.startswith('ERR'):
tail= tail[4:]
response=ResponseError(tail, cmd_line)else:raise ResponseError('Unknown response type %s' %head, cmd_line)returnresponse
asyncdef listen(self, callback=None, exit_callback=None):
cmd_listen= CmdLine('LISTEN')whileself.subscribed:try:
data= await self.stream.read_until(b'\r\n')exceptException as e:#Maybe wrong!
importlogging
logging.exception(e)
data=Noneif data isNone:#If disconnected from the redis server clear the list
#of subscriber this client has subscribed to
channels =self.subscribed
self.subscribed=set()#send a message to caller:
#Message(kind='disconnect', channel=set(channel1, ...))
callback(reply_pubsub_message(('disconnect', channels)))returnresponse=await self.process_data(data, cmd_listen)ifisinstance(response, Exception):raiseresponse
result=self.format_reply(cmd_listen, response)if result and result.kind in ('subscribe', 'psubscribe'):
self.on_subscribed(result)if result and result.kind in ('unsubscribe', 'punsubscribe'):
self.on_unsubscribed([result.channel])
callback(result)defformat_reply(self, cmd_line, data):if cmd_line.cmd not inREPLY_MAP:returndatatry:
res= REPLY_MAP[cmd_line.cmd](data, *cmd_line.args, **cmd_line.kwargs)exceptException as e:raiseResponseError('failed to format reply to %s, raw data: %s; err message: %s'
%(cmd_line, data, e), cmd_line
)returnresdefencode(self, value):if notisinstance(value, str):
value=str(value)
value= value.encode('utf-8')returnvaluedef format_command(self, *tokens, **kwargs):
cmds=[]for t intokens:
e_t=self.encode(t)
e_t_s=to_basestring(e_t)
cmds.append('$%s\r\n%s\r\n' %(len(e_t), e_t_s))
data= '*%s\r\n%s' % (len(tokens), ''.join(cmds))return bytes(data, encoding='utf-8')
asyncdef execute_command(self, cmd, *args, **kwargs):
result=None
cmd_line= CmdLine(cmd, *args, **kwargs)
n_tries= 2
while n_tries >0:
n_tries-= 1
if not self.subscribed and cmd not in ('AUTH', 'SELECT'):if self.password and self.info.get('pass', None) !=self.password:
await self.auth(self.password)if self.selected_db and self.info.get('db', 0) !=self.selected_db:
await self.select(self.selected_db)
command= self.format_command(cmd, *args, **kwargs)
await self.stream.write(command)
listening= ((cmd in PUB_SUB_COMMANDS) or (self.subscribed and cmd == 'PUBLISH'))iflistening:breakdata= await self.stream.read_until(b'\r\n')
resp=await self.process_data(data, cmd_line)
result=self.format_reply(cmd_line, resp)returnresult### MAINTENANCE
def bgrewriteaof(self, callback=None):
self.execute_command('BGREWRITEAOF', callback=callback)def dbsize(self, callback=None):
self.execute_command('DBSIZE', callback=callback)def flushall(self, callback=None):
self.execute_command('FLUSHALL', callback=callback)def flushdb(self, callback=None):
self.execute_command('FLUSHDB', callback=callback)def ping(self, callback=None):
self.execute_command('PING', callback=callback)def object(self, infotype, key, callback=None):
self.execute_command('OBJECT', infotype, key, callback=callback)def info(self, section_name=None, callback=None):
args= ('INFO',)ifsection_name:
args+=(section_name,)
self.execute_command(*args, callback=callback)def echo(self, value, callback=None):
self.execute_command('ECHO', value, callback=callback)def time(self, callback=None):"""Returns the server time as a 2-item tuple of ints:
(seconds since epoch, microseconds into this second)."""self.execute_command('TIME', callback=callback)def select(self, db, callback=None):
self.selected_db=db#if self.connection.info.get('db', None) != db:
#self.connection.info['db'] = db
#self.execute_command('SELECT', '%s' % db, callback=callback)
#elif callback:
#callback(True)
def shutdown(self, callback=None):
self.execute_command('SHUTDOWN', callback=callback)def save(self, callback=None):
self.execute_command('SAVE', callback=callback)def bgsave(self, callback=None):
self.execute_command('BGSAVE', callback=callback)def lastsave(self, callback=None):
self.execute_command('LASTSAVE', callback=callback)def keys(self, pattern='*', callback=None):
self.execute_command('KEYS', pattern, callback=callback)
asyncdef auth(self, password, callback=None):
self.password=passwordif self.info.get('pass', None) !=password:
self.info['pass'] =password
await self.execute_command('AUTH', password, callback=callback)#elif callback:
#callback(True)
### BASIC KEY COMMANDS
def append(self, key, value, callback=None):
self.execute_command('APPEND', key, value, callback=callback)def getrange(self, key, start, end, callback=None):"""Returns the substring of the string value stored at ``key``,
determined by the offsets ``start`` and ``end`` (both are inclusive)"""self.execute_command('GETRANGE', key, start, end, callback=callback)def expire(self, key, ttl, callback=None):
self.execute_command('EXPIRE', key, ttl, callback=callback)def expireat(self, key, when, callback=None):"""Sets an expire flag on ``key``. ``when`` can be represented
as an integer indicating unix time or a Python datetime.datetime object."""
ifisinstance(when, datetime.datetime):
when=int(mod_time.mktime(when.timetuple()))
self.execute_command('EXPIREAT', key, when, callback=callback)def ttl(self, key, callback=None):
self.execute_command('TTL', key, callback=callback)def type(self, key, callback=None):
self.execute_command('TYPE', key, callback=callback)def randomkey(self, callback=None):
self.execute_command('RANDOMKEY', callback=callback)def rename(self, src, dst, callback=None):
self.execute_command('RENAME', src, dst, callback=callback)def renamenx(self, src, dst, callback=None):
self.execute_command('RENAMENX', src, dst, callback=callback)def move(self, key, db, callback=None):
self.execute_command('MOVE', key, db, callback=callback)def persist(self, key, callback=None):
self.execute_command('PERSIST', key, callback=callback)def pexpire(self, key, time, callback=None):"""Set an expire flag on key ``key`` for ``time`` milliseconds.
``time`` can be represented by an integer or a Python timedelta
object."""
ifisinstance(time, datetime.timedelta):
ms= int(time.microseconds / 1000)
time= time.seconds + time.days * 24 * 3600 * 1000 +ms
self.execute_command('PEXPIRE', key, time, callback=callback)def pexpireat(self, key, when, callback=None):"""Set an expire flag on key ``key``. ``when`` can be represented
as an integer representing unix time in milliseconds (unix time * 1000)
or a Python datetime.datetime object."""
ifisinstance(when, datetime.datetime):
ms= int(when.microsecond / 1000)
when= int(mod_time.mktime(when.timetuple())) * 1000 +ms
self.execute_command('PEXPIREAT', key, when, callback=callback)def pttl(self, key, callback=None):"Returns the number of milliseconds until the key will expire"self.execute_command('PTTL', key, callback=callback)def substr(self, key, start, end, callback=None):
self.execute_command('SUBSTR', key, start, end, callback=callback)def delete(self, *keys, **kwargs):
self.execute_command('DEL', *keys, callback=kwargs.get('callback'))
asyncdef set(self, key, value, expire=None, pexpire=None,
only_if_not_exists=False, only_if_exists=False, callback=None):
args=[]if expire is notNone:
args.extend(("EX", expire))if pexpire is notNone:
args.extend(("PX", pexpire))if only_if_not_exists andonly_if_exists:raise ValueError("only_if_not_exists and only_if_exists"
"cannot be true simultaneously")ifonly_if_not_exists:
args.append("NX")ifonly_if_exists:
args.append("XX")
await self.execute_command('SET', key, value, *args, callback=callback)def setex(self, key, ttl, value, callback=None):
self.execute_command('SETEX', key, ttl, value, callback=callback)def setnx(self, key, value, callback=None):
self.execute_command('SETNX', key, value, callback=callback)def setrange(self, key, offset, value, callback=None):
self.execute_command('SETRANGE', key, offset, value, callback=callback)def strlen(self, key, callback=None):
self.execute_command('STRLEN', key, callback=callback)def mset(self, mapping, callback=None):
items= [i for k, v in mapping.items() for i in(k, v)]
self.execute_command('MSET', *items, callback=callback)def msetnx(self, mapping, callback=None):
items= [i for k, v in mapping.items() for i in(k, v)]
self.execute_command('MSETNX', *items, callback=callback)
asyncdef get(self, key, callback=None):return await self.execute_command('GET', key, callback=callback)def mget(self, keys, callback=None):
self.execute_command('MGET', *keys, callback=callback)def getset(self, key, value, callback=None):
self.execute_command('GETSET', key, value, callback=callback)def exists(self, key, callback=None):
self.execute_command('EXISTS', key, callback=callback)def sort(self, key, start=None, num=None, by=None, get=None, desc=False,
alpha=False, store=None, callback=None):if ((start is not None and num is None) or(numis not None and start isNone)):raise ValueError("``start`` and ``num`` must both be specified")
tokens=[key]if by is notNone:
tokens.append('BY')
tokens.append(by)if start is not None and num is notNone:
tokens.append('LIMIT')
tokens.append(start)
tokens.append(num)if get is notNone:
tokens.append('GET')
tokens.append(get)ifdesc:
tokens.append('DESC')ifalpha:
tokens.append('ALPHA')if store is notNone:
tokens.append('STORE')
tokens.append(store)
self.execute_command('SORT', *tokens, callback=callback)def getbit(self, key, offset, callback=None):
self.execute_command('GETBIT', key, offset, callback=callback)def setbit(self, key, offset, value, callback=None):
self.execute_command('SETBIT', key, offset, value, callback=callback)def bitcount(self, key, start=None, end=None, callback=None):
args= [a for a in (key, start, end) if a is notNone]
kwargs= {'callback': callback}
self.execute_command('BITCOUNT', *args, **kwargs)def bitop(self, operation, dest, *keys, **kwargs):"""Perform a bitwise operation using ``operation`` between ``keys`` and
store the result in ``dest``."""kwargs= {'callback': kwargs.get('callback', None)}
self.execute_command('BITOP', operation, dest, *keys, **kwargs)### COUNTERS COMMANDS
def incr(self, key, callback=None):
self.execute_command('INCR', key, callback=callback)def decr(self, key, callback=None):
self.execute_command('DECR', key, callback=callback)def incrby(self, key, amount, callback=None):
self.execute_command('INCRBY', key, amount, callback=callback)def incrbyfloat(self, key, amount=1.0, callback=None):
self.execute_command('INCRBYFLOAT', key, amount, callback=callback)def decrby(self, key, amount, callback=None):
self.execute_command('DECRBY', key, amount, callback=callback)### LIST COMMANDS
def blpop(self, keys, timeout=0, callback=None):
tokens=to_list(keys)
tokens.append(timeout)
self.execute_command('BLPOP', *tokens, callback=callback)def brpop(self, keys, timeout=0, callback=None):
tokens=to_list(keys)
tokens.append(timeout)
self.execute_command('BRPOP', *tokens, callback=callback)def brpoplpush(self, src, dst, timeout=1, callback=None):
tokens=[src, dst, timeout]
self.execute_command('BRPOPLPUSH', *tokens, callback=callback)def lindex(self, key, index, callback=None):
self.execute_command('LINDEX', key, index, callback=callback)def llen(self, key, callback=None):
self.execute_command('LLEN', key, callback=callback)def lrange(self, key, start, end, callback=None):
self.execute_command('LRANGE', key, start, end, callback=callback)def lrem(self, key, value, num=0, callback=None):
self.execute_command('LREM', key, num, value, callback=callback)def lset(self, key, index, value, callback=None):
self.execute_command('LSET', key, index, value, callback=callback)def ltrim(self, key, start, end, callback=None):
self.execute_command('LTRIM', key, start, end, callback=callback)def lpush(self, key, *values, **kwargs):
callback= kwargs.get('callback', None)
self.execute_command('LPUSH', key, *values, callback=callback)def lpushx(self, key, value, callback=None):
self.execute_command('LPUSHX', key, value, callback=callback)def linsert(self, key, where, refvalue, value, callback=None):
self.execute_command('LINSERT', key, where, refvalue, value,
callback=callback)def rpush(self, key, *values, **kwargs):
callback= kwargs.get('callback', None)
self.execute_command('RPUSH', key, *values, callback=callback)def rpushx(self, key, value, **kwargs):"Push ``value`` onto the tail of the list ``name`` if ``name`` exists"callback= kwargs.get('callback', None)
self.execute_command('RPUSHX', key, value, callback=callback)def lpop(self, key, callback=None):
self.execute_command('LPOP', key, callback=callback)def rpop(self, key, callback=None):
self.execute_command('RPOP', key, callback=callback)def rpoplpush(self, src, dst, callback=None):
self.execute_command('RPOPLPUSH', src, dst, callback=callback)### SET COMMANDS
def sadd(self, key, *values, **kwargs):
callback= kwargs.get('callback', None)
self.execute_command('SADD', key, *values, callback=callback)def srem(self, key, *values, **kwargs):
callback= kwargs.get('callback', None)
self.execute_command('SREM', key, *values, callback=callback)def scard(self, key, callback=None):
self.execute_command('SCARD', key, callback=callback)def spop(self, key, callback=None):
self.execute_command('SPOP', key, callback=callback)def smove(self, src, dst, value, callback=None):
self.execute_command('SMOVE', src, dst, value, callback=callback)def sismember(self, key, value, callback=None):
self.execute_command('SISMEMBER', key, value, callback=callback)def smembers(self, key, callback=None):
self.execute_command('SMEMBERS', key, callback=callback)def srandmember(self, key, number=None, callback=None):ifnumber:
self.execute_command('SRANDMEMBER', key, number, callback=callback)else:
self.execute_command('SRANDMEMBER', key, callback=callback)def sinter(self, keys, callback=None):
self.execute_command('SINTER', *keys, callback=callback)def sdiff(self, keys, callback=None):
self.execute_command('SDIFF', *keys, callback=callback)def sunion(self, keys, callback=None):
self.execute_command('SUNION', *keys, callback=callback)def sinterstore(self, keys, dst, callback=None):
self.execute_command('SINTERSTORE', dst, *keys, callback=callback)def sunionstore(self, keys, dst, callback=None):
self.execute_command('SUNIONSTORE', dst, *keys, callback=callback)def sdiffstore(self, keys, dst, callback=None):
self.execute_command('SDIFFSTORE', dst, *keys, callback=callback)### SORTED SET COMMANDS
def zadd(self, key, *score_value, **kwargs):
callback= kwargs.get('callback', None)
self.execute_command('ZADD', key, *score_value, callback=callback)def zcard(self, key, callback=None):
self.execute_command('ZCARD', key, callback=callback)def zincrby(self, key, value, amount, callback=None):
self.execute_command('ZINCRBY', key, amount, value, callback=callback)def zrank(self, key, value, callback=None):
self.execute_command('ZRANK', key, value, callback=callback)def zrevrank(self, key, value, callback=None):
self.execute_command('ZREVRANK', key, value, callback=callback)def zrem(self, key, *values, **kwargs):
callback= kwargs.get('callback', None)
self.execute_command('ZREM', key, *values, callback=callback)def zcount(self, key, start, end, callback=None):
self.execute_command('ZCOUNT', key, start, end, callback=callback)def zscore(self, key, value, callback=None):
self.execute_command('ZSCORE', key, value, callback=callback)def zrange(self, key, start, num, with_scores=True, callback=None):
tokens=[key, start, num]ifwith_scores:
tokens.append('WITHSCORES')
self.execute_command('ZRANGE', *tokens, callback=callback)def zrevrange(self, key, start, num, with_scores, callback=None):
tokens=[key, start, num]ifwith_scores:
tokens.append('WITHSCORES')
self.execute_command('ZREVRANGE', *tokens, callback=callback)def zrangebyscore(self, key, start, end, offset=None, limit=None,
with_scores=False, callback=None):
tokens=[key, start, end]if offset is notNone:
tokens.append('LIMIT')
tokens.append(offset)
tokens.append(limit)ifwith_scores:
tokens.append('WITHSCORES')
self.execute_command('ZRANGEBYSCORE', *tokens, callback=callback)def zrevrangebyscore(self, key, end, start, offset=None, limit=None,
with_scores=False, callback=None):
tokens=[key, end, start]if offset is notNone:
tokens.append('LIMIT')
tokens.append(offset)
tokens.append(limit)ifwith_scores:
tokens.append('WITHSCORES')
self.execute_command('ZREVRANGEBYSCORE', *tokens, callback=callback)def zremrangebyrank(self, key, start, end, callback=None):
self.execute_command('ZREMRANGEBYRANK', key, start, end,
callback=callback)def zremrangebyscore(self, key, start, end, callback=None):
self.execute_command('ZREMRANGEBYSCORE', key, start, end,
callback=callback)def zinterstore(self, dest, keys, aggregate=None, callback=None):return self._zaggregate('ZINTERSTORE', dest, keys, aggregate, callback)def zunionstore(self, dest, keys, aggregate=None, callback=None):return self._zaggregate('ZUNIONSTORE', dest, keys, aggregate, callback)def_zaggregate(self, command, dest, keys, aggregate, callback):
tokens=[dest, len(keys)]ifisinstance(keys, dict):
items=list(keys.items())
keys= [i[0] for i initems]
weights= [i[1] for i initems]else:
weights=None
tokens.extend(keys)ifweights:
tokens.append('WEIGHTS')
tokens.extend(weights)ifaggregate:
tokens.append('AGGREGATE')
tokens.append(aggregate)
self.execute_command(command,*tokens, callback=callback)### HASH COMMANDS
def hgetall(self, key, callback=None):
self.execute_command('HGETALL', key, callback=callback)def hmset(self, key, mapping, callback=None):
items= [i for k, v in mapping.items() for i in(k, v)]
self.execute_command('HMSET', key, *items, callback=callback)def hset(self, key, field, value, callback=None):
self.execute_command('HSET', key, field, value, callback=callback)def hsetnx(self, key, field, value, callback=None):
self.execute_command('HSETNX', key, field, value, callback=callback)def hget(self, key, field, callback=None):
self.execute_command('HGET', key, field, callback=callback)def hdel(self, key, *fields, **kwargs):
callback= kwargs.get('callback')
self.execute_command('HDEL', key, *fields, callback=callback)def hlen(self, key, callback=None):
self.execute_command('HLEN', key, callback=callback)def hexists(self, key, field, callback=None):
self.execute_command('HEXISTS', key, field, callback=callback)def hincrby(self, key, field, amount=1, callback=None):
self.execute_command('HINCRBY', key, field, amount, callback=callback)def hincrbyfloat(self, key, field, amount=1.0, callback=None):
self.execute_command('HINCRBYFLOAT', key, field, amount,
callback=callback)def hkeys(self, key, callback=None):
self.execute_command('HKEYS', key, callback=callback)def hmget(self, key, fields, callback=None):
self.execute_command('HMGET', key, *fields, callback=callback)def hvals(self, key, callback=None):
self.execute_command('HVALS', key, callback=callback)### SCAN COMMANDS
def scan(self, cursor, count=None, match=None, callback=None):
self._scan('SCAN', cursor, count, match, callback)def hscan(self, key, cursor, count=None, match=None, callback=None):
self._scan('HSCAN', cursor, count, match, callback, key=key)def sscan(self, key, cursor, count=None, match=None, callback=None):
self._scan('SSCAN', cursor, count, match, callback, key=key)def zscan(self, key, cursor, count=None, match=None, callback=None):
self._scan('ZSCAN', cursor, count, match, callback, key=key)def _scan(self, cmd, cursor, count, match, callback, key=None):
tokens=[cmd]
keyandtokens.append(key)
tokens.append(cursor)
matchand tokens.extend(['MATCH', match])
countand tokens.extend(['COUNT', count])
self.execute_command(*tokens, callback=callback)### GEO COMMANDS
def geoadd(self, key, longitude, latitude, member, *args, **kwargs):
self.execute_command('GEOADD', key, longitude, latitude, member, *args, **kwargs)def geodist(self, key, member1, member2, unit='m', callback=None):
self.execute_command('GEODIST', key, member1, member2, unit, callback=callback)def geohash(self, key, member, *args, **kwargs):
self.execute_command('GEOHASH', key, member, *args, **kwargs)def geopos(self, key, member, *args, **kwargs):
self.execute_command('GEOPOS', key, member, *args, **kwargs)def georadius(self, key, longitude, latitude, radius, unit='m',
with_coord=False, with_dist=False, with_hash=False,
count=None, sort=None, callback=None):
args=[]ifwith_coord:
args.append('WITHCOORD')ifwith_dist:
args.append('WITHDIST')ifwith_hash:
args.append('WITHHASH')if count and count >0:
args.append(count)if sort and sort in ['ASC', 'DESC']:
args.append(sort)
self.execute_command('GEORADIUS', key, longitude, latitude, radius, unit, callback=callback, *args)def georadiusbymember(self, key, member, radius, unit='m',
with_coord=False, with_dist=False, with_hash=False,
count=None, sort=None, callback=None):
args=[]ifwith_coord:
args.append('WITHCOORD')ifwith_dist:
args.append('WITHDIST')ifwith_hash:
args.append('WITHHASH')if count and count >0:
args.append(count)if sort and sort in ['ASC', 'DESC']:
args.append(sort)
self.execute_command('GEORADIUSBYMEMBER', key, member, radius, unit, callback=callback, *args)### PUBSUB
asyncdef subscribe(self, channels, callback=None):
await self._subscribe('SUBSCRIBE', channels, callback=callback)
asyncdef psubscribe(self, channels, callback=None):
await self._subscribe('PSUBSCRIBE', channels, callback=callback)
asyncdef _subscribe(self, cmd, channels, callback=None):ifisinstance(channels, str):
channels=[channels]if notself.subscribed:
self.on_subscribed(Message(kind='subscribe',
channel=channels[0],
body=None,
pattern=None)
)for channel inchannels:
self.subscribe_callbacks.append((channel, None))#Do not execute the same callback multiple times
await self.execute_command(cmd,*channels, callback=callback)defon_subscribed(self, result):
self.subscribed.add(result.channel)def on_unsubscribed(self, channels, *args, **kwargs):
channels=set(channels)
self.subscribed-=channels
asyncdef unsubscribe(self, channels, callback=None):
await self._unsubscribe('UNSUBSCRIBE', channels, callback=callback)
asyncdef punsubscribe(self, channels, callback=None):
await self._unsubscribe('PUNSUBSCRIBE', channels, callback=callback)
asyncdef _unsubscribe(self, cmd, channels, callback=None):ifisinstance(channels, str):
channels=[channels]
await self.execute_command(cmd,*channels)
asyncdef publish(self, channel, message, callback=None):
await self.execute_command('PUBLISH', channel, message, callback=callback)### CAS
def watch(self, *key_names, **kwargs):
callback= kwargs.get('callback', None)
self.execute_command('WATCH', *key_names, callback=callback)def unwatch(self, callback=None):
self.execute_command('UNWATCH', callback=callback)### SCRIPTING COMMANDS
def eval(self, script, keys=None, args=None, callback=None):if keys isNone:
keys=[]if args isNone:
args=[]
num_keys=len(keys)
_args= keys +args
self.execute_command('EVAL', script, num_keys,*_args, callback=callback)def evalsha(self, shahash, keys=None, args=None, callback=None):if keys isNone:
keys=[]if args isNone:
args=[]
num_keys=len(keys)
keys.extend(args)
self.execute_command('EVALSHA', shahash, num_keys,*keys, callback=callback)def script_exists(self, shahashes, callback=None):#not yet implemented in the redis protocol
self.execute_command('SCRIPT EXISTS', *shahashes, callback=callback)def script_flush(self, callback=None):#not yet implemented in the redis protocol
self.execute_command('SCRIPT FLUSH', callback=callback, verbose=True)def script_kill(self, callback=None):#not yet implemented in the redis protocol
self.execute_command('SCRIPT KILL', callback=callback)def script_load(self, script, callback=None):#not yet implemented in the redis protocol
self.execute_command('SCRIPT LOAD', script, callback=callback)defdisconnect(self):
self.stream.close()
self.client.close()
asyncdefmain():#测试get and set
"""host="192.168.1.252",
port=6379,
password="123456""""client= TornadoRedis(host="192.168.1.252", port=6379, password="123456", max_connections=2)
conn1=await client.get_connection()
client.release(conn1)
conn2=await client.get_connection()
conn3=await client.get_connection()
await conn1.set(key="abc", value="data", expire=100)
tmp= await conn1.get("abc")print(tmp)if __name__ == "__main__":
loop=asyncio.get_event_loop()#执行coroutine
loop.run_until_complete(main())
loop.close()