tornado-redis入门

tornado-redis github 地址:https://github.com/leporo/tornado-redis

介绍
tornado-redis 包,一个 tornado 可用的异步 redis client。但已不在维护。
tornado-redis连接数据库
1.普通连接(未使用连接池池)

import tornadoredis
c = tornadoredis.Client(host="127.0.0.1",port=6379)

# 测试是否连接成功,写一个key,并查看redis数据库是否存在该key
c.set("name","zhangsan")

执行结果

127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379>

2.连接池连接

具体连接池的概念就不说了,很简单

import tornadoredis

CONNECTION_POOL = tornadoredis.ConnectionPool(max_connections=100, wait_for_available=True)
c = tornadoredis.Client(host="127.0.0.1", port="6379", connection_pool=CONNECTION_POOL)
# 测试是否连接成功,写一个key,并查看redis数据库是否存在该key
c.set("age",18)

执行结果

127.0.0.1:6379> get age
"18"
127.0.0.1:6379>

tornado 可用的异步 redis 客户端,使用很方便。
下面的例子介绍了怎么使用管道设置值和读取值的简单操作

复制代码

#coding:utf-8
import tornadoredis
import tornado.httpserver
import tornado.web
import tornado.ioloop
import tornado.gen

#设置连接池
CONNECTION_POOL = tornadoredis.ConnectionPool(max_connections=500,wait_for_available=True)
# 生成一个客户端,这里并没有给出host和port,使用的默认的参数,我的redis使用默认的参数也可以连接上
# Client的参数为
# def __init__(self, host='localhost', port=6379, unix_socket_path=None,
#                  password=None, selected_db=None, io_loop=None,
#                  connection_pool=None):
c = tornadoredis.Client(connection_pool=CONNECTION_POOL)

class MainHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):
        name = yield tornado.gen.Task(c.get,'name')
        age = yield tornado.gen.Task(c.get,'age')
        height = yield tornado.gen.Task(c.get,'height')
        self.finish("name: {0} age: {1} height: {2}".format(name, age, height))


application = tornado.web.Application([
    (r'/', MainHandler),
])

@tornado.gen.engine
def create_test_data():
    c = tornadoredis.Client()
    # 使用管道设置值
    with c.pipeline() as pipe:
        pipe.set('name', 'zhangsan')
        pipe.set('age', '10')
        pipe.set('height', '1.8')
        yield tornado.gen.Task(pipe.execute)


if __name__ == '__main__':
    # Start the data initialization routine
    create_test_data()
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    print 'Demo is runing at 0.0.0.0:8888\nQuit the demo with CONTROL-C'
    tornado.ioloop.IOLoop.instance().start()

复制代码

执行结果

name: zhangsan age: 10 height: 1.8

1、scan的使用

yield tornado.gen.Task(c.scan, cursor = 0, match = "*:{0}".format(sha1))

2、incr的使用

tornado.gen.Task(async.incr, 'file_upload_idx')

3、hmget的使用

yield tornado.gen.Task(async.hmget,key,["uid","file_type"])

注意

在使用tornado-redis的时候一定要在形如class MainHandler(tornado.web.RequestHandler)中使用,不要想着脱离它再使用,否则会出现很多的错误,这个坑我已经踩过了,大家注意,如果在其他的地方使用可以选择使用redis这个包。

tornado-redis 改编

tornado-redis改编,使用tornado  tcpclient 改变了原来的网络io

支持python3.5、tornado6.0,

已测试

1、redis  set  和 get

2、redis 订阅和发布

复制代码

#!/usr/bin/env python
# encoding: utf-8
import asyncio
import datetime
import collections
import time as mod_time
from asyncio import Future
from collections import namedtuple, deque
from tornado.tcpclient import TCPClient
from tornado.escape import to_unicode, to_basestring


class RedisError(Exception):
    pass


class ConnectionError(RedisError):
    pass


class RequestError(RedisError):
    def __init__(self, message, cmd_line=None):
        self.message = message
        self.cmd_line = cmd_line

    def __repr__(self):
        if self.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'))


def string_keys_to_dict(key_string, callback):
    return dict([(key, callback) for key in key_string.split()])


def dict_merge(*dicts):
    merged = {}
    for d in dicts:
        merged.update(d)
    return merged


def reply_to_bool(r, *args, **kwargs):
    return bool(r)


def make_reply_assert_msg(msg):
    def reply_assert_msg(r, *args, **kwargs):
        return r == msg

    return reply_assert_msg


def reply_set(r, *args, **kwargs):
    return set(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 else None


def reply_number(r, *args, **kwargs):
    if r is not None:
        num = float(r)
        if not num.is_integer():
            return num
        else:
            return int(num)
    return None


def reply_datetime(r, *args, **kwargs):
    return datetime.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 = channel
    elif len(r) == 4:
        (kind, pattern, channel, body) = r
    elif len(r) == 2:
        (kind, channel) = r
        body = pattern = None
    else:
        raise ValueError('Invalid number of arguments')
    return Message(kind, channel, body, pattern)


def reply_zset(r, *args, **kwargs):
    if r and 'WITHSCORES' in args:
        return reply_zset_withscores(r, *args, **kwargs)
    else:
        return r


def 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):
    return dict(list(zip(fields, r)))


def reply_info(response, *args):
    info = {}

    def get_value(value):
        # Does this string contain subvalues?
        if (',' not in value) or ('=' not in value):
            return value
        sub_dict = {}
        for item in value.split(','):
            k, v = item.split('=')
            try:
                sub_dict[k] = int(v)
            except ValueError:
                sub_dict[k] = v
        return sub_dict

    for line in response.splitlines():
        line = line.strip()
        if line and not line.startswith('#'):
            key, value = line.split(':')
            try:
                info[key] = int(value)
            except ValueError:
                info[key] = get_value(value)
    return info


def reply_ttl(r, *args, **kwargs):
    return r != -1 and r or None


def 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 in zip(funcs, r)]

    return reply_fn


def reply_coords(r, *args, **kwargs):
    return [(float(c[0]), float(c[1])) for c in r]


def reply_geo_radius(r, *args, **kwargs):
    geo_data = []
    for member in r:
        name = member[0]
        dist = coords = hs = None

        if 'WITHDIST' in args:
            dist = float(member[1])

        if 'WITHHASH' in args and 'WITHDIST' in args:
            hs = int(member[2])
        elif 'WITHHASH' in args:
            hs = int(member[1])

        if 'WITHCOORD' in args and 'WITHHASH' in args and 'WITHDIST' in args:
            coords = (float(member[3][0]), float(member[3][1]))
        elif 'WITHCOORD' in args and ('WITHHASH' in args or 'WITHDIST' in args):
            coords = (float(member[2][0]), float(member[2][1]))
        elif 'WITHCOORD' in args:
            coords = (float(member[1][0]), float(member[1][1]))

        geo_data.append(GeoData(name, dist, coords, hs))
    return geo_data


def to_list(source):
    if isinstance(source, str):
        return [source]
    else:
        return list(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)}
)


class ResponseError(RedisError):
    def __init__(self, message, cmd_line=None):
        self.message = message
        self.cmd_line = cmd_line

    def __repr__(self):
        if self.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__


class InvalidResponse(RedisError):
    pass


class LockError(RedisError):
    "Errors thrown from the Lock"
    pass


class CmdLine(object):
    def __init__(self, cmd, *args, **kwargs):
        self.cmd = cmd
        self.args = args
        self.kwargs = kwargs

    def __repr__(self):
        return self.cmd + '(' + str(self.args) + ',' + str(self.kwargs) + ')'


class TornadoRedis:
    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 2048
        self._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 in self._available_connections.union(self._used_connections):
            conn.disconnect()
        for conn in self._waiting_connections:
            conn.cancel()  # Cancel the future and schedule callbacks.

    async def get_connection(self):
        """
        Returns a pooled Redis server connection
        """
        try:
            connection = self._available_connections.pop()
        except KeyError:
            connection = self.make_connection()
        if isinstance(connection, Future):
            connection = await connection
        if connection:
            await connection.connect()
            self._used_connections.add(connection)
        return connection

    def make_connection(self):
        """
        Creates a new connection to Redis server
        """
        if self._created_connections >= self.max_connections:
            f = Future()
            self._waiting_connections.add(f)
            return f
        self._created_connections += 1
        return Connection(self.host, self.port, selected_db=self.selected_db, password=self.password)

    def release(self, connection):
        """
        Releases the connection back to the pool
        """
        if self._waiting_connections:
            waiting = self._waiting_connections.pop()
            waiting.set_result(connection)
        else:
            try:
                self._used_connections.remove(connection)
            except (KeyError, ValueError):
                pass
            self._available_connections.add(connection)


class Connection(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 or 0

        self._stream = None
        self.client = TCPClient()
        self.stream = None
        self.info = {'db': 0, 'pass': None}

    def __del__(self):
        self.disconnect()

    async def connect(self):
        if not self.stream:
            self.stream = await self.client.connect(host=self.host, port=self.port)

    async def _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)
        if isinstance(response, Exception):
            raise response
        if not response:
            raise ResponseError('EmptyResponse')
        else:
            response = to_unicode(response)
            response = response[:-2]
        return response

    async def consume_multibulk(self, length, cmd_line):
        tokens = []
        while len(tokens) < length:
            data = await self.stream.read_until(b'\r\n')
            if not data:
                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)
        return tokens

    async def process_data(self, data, cmd_line):
        data = to_basestring(data)
        data = data[:-2]  # strip \r\n

        if data == '$-1':
            response = None
        elif 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 = tail
            elif 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)
        return response

    async def listen(self, callback=None, exit_callback=None):
        cmd_listen = CmdLine('LISTEN')
        while self.subscribed:
            try:
                data = await self.stream.read_until(b'\r\n')
            except Exception as e:
                # Maybe wrong!
                import logging
                logging.exception(e)

                data = None

            if data is None:
                # 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)))
                return

            response = await self.process_data(data, cmd_listen)

            if isinstance(response, Exception):
                raise response

            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)

    def format_reply(self, cmd_line, data):
        if cmd_line.cmd not in REPLY_MAP:
            return data
        try:
            res = REPLY_MAP[cmd_line.cmd](data, *cmd_line.args, **cmd_line.kwargs)
        except Exception as e:
            raise ResponseError(
                'failed to format reply to %s, raw data: %s; err message: %s'
                % (cmd_line, data, e), cmd_line
            )
        return res

    def encode(self, value):
        if not isinstance(value, str):
            value = str(value)
        value = value.encode('utf-8')
        return value

    def format_command(self, *tokens, **kwargs):
        cmds = []
        for t in tokens:
            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')

    async def 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'))
            if listening:
                break
            data = await self.stream.read_until(b'\r\n')

            resp = await self.process_data(data, cmd_line)
            result = self.format_reply(cmd_line, resp)
            return result

        ### 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',)
        if section_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)

    async def auth(self, password, callback=None):
        self.password = password
        if 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.
        """
        if isinstance(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.
        """
        if isinstance(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.
        """
        if isinstance(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'))

    async def set(self, key, value, expire=None, pexpire=None,
                  only_if_not_exists=False, only_if_exists=False, callback=None):
        args = []
        if expire is not None:
            args.extend(("EX", expire))
        if pexpire is not None:
            args.extend(("PX", pexpire))
        if only_if_not_exists and only_if_exists:
            raise ValueError("only_if_not_exists and only_if_exists "
                             "cannot be true simultaneously")
        if only_if_not_exists:
            args.append("NX")
        if only_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)

    async def 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
                (num is not None and start is None)):
            raise ValueError("``start`` and ``num`` must both be specified")

        tokens = [key]
        if by is not None:
            tokens.append('BY')
            tokens.append(by)
        if start is not None and num is not None:
            tokens.append('LIMIT')
            tokens.append(start)
            tokens.append(num)
        if get is not None:
            tokens.append('GET')
            tokens.append(get)
        if desc:
            tokens.append('DESC')
        if alpha:
            tokens.append('ALPHA')
        if store is not None:
            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 not None]
        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):
        if number:
            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]
        if with_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]
        if with_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 not None:
            tokens.append('LIMIT')
            tokens.append(offset)
            tokens.append(limit)
        if with_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 not None:
            tokens.append('LIMIT')
            tokens.append(offset)
            tokens.append(limit)
        if with_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)]
        if isinstance(keys, dict):
            items = list(keys.items())
            keys = [i[0] for i in items]
            weights = [i[1] for i in items]
        else:
            weights = None
        tokens.extend(keys)
        if weights:
            tokens.append('WEIGHTS')
            tokens.extend(weights)
        if aggregate:
            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]
        key and tokens.append(key)
        tokens.append(cursor)
        match and tokens.extend(['MATCH', match])
        count and 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 = []

        if with_coord:
            args.append('WITHCOORD')
        if with_dist:
            args.append('WITHDIST')
        if with_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 = []

        if with_coord:
            args.append('WITHCOORD')
        if with_dist:
            args.append('WITHDIST')
        if with_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

    async def subscribe(self, channels, callback=None):
        await self._subscribe('SUBSCRIBE', channels, callback=callback)

    async def psubscribe(self, channels, callback=None):
        await self._subscribe('PSUBSCRIBE', channels, callback=callback)

    async def _subscribe(self, cmd, channels, callback=None):
        if isinstance(channels, str):
            channels = [channels]
        if not self.subscribed:
            self.on_subscribed(Message(kind='subscribe',
                                       channel=channels[0],
                                       body=None,
                                       pattern=None)
                               )
        for channel in channels:
            self.subscribe_callbacks.append((channel, None))
            # Do not execute the same callback multiple times

        await self.execute_command(cmd, *channels, callback=callback)

    def on_subscribed(self, result):
        self.subscribed.add(result.channel)

    def on_unsubscribed(self, channels, *args, **kwargs):
        channels = set(channels)
        self.subscribed -= channels

    async def unsubscribe(self, channels, callback=None):
        await self._unsubscribe('UNSUBSCRIBE', channels, callback=callback)

    async def punsubscribe(self, channels, callback=None):
        await self._unsubscribe('PUNSUBSCRIBE', channels, callback=callback)

    async def _unsubscribe(self, cmd, channels, callback=None):
        if isinstance(channels, str):
            channels = [channels]
        await self.execute_command(cmd, *channels)

    async def 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 is None:
            keys = []
        if args is None:
            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 is None:
            keys = []
        if args is None:
            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)

    def disconnect(self):
        self.stream.close()
        self.client.close()


async def main():
    # 测试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()

复制代码

 

websocket

复制代码

from __future__ import print_function
import tornado.httpserver
import tornado.web
import tornado.websocket
import tornado.ioloop
import tornado.gen

from test_my import TornadoRedis

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("template.html", title="PubSub + WebSocket Demo")


class NewMessageHandler(tornado.web.RequestHandler):

    async def post(self,*args):
        c = TornadoRedis()
        await c.connect()
        message =  self.get_body_argument("message")
        await c.publish('test_channel', message)
        self.set_header('Content-Type', 'text/plain')
        self.write('sent: %s' % (message,))


class MessageHandler(tornado.websocket.WebSocketHandler):

    def check_origin(self, origin: str) -> bool:
        return True
        
    async def open(self):
        await self.listen()

    async def listen(self):
        self.client = TornadoRedis()
        await self.client.connect()
        await self.client.subscribe( 'test_channel')
        await self.client.listen(self.on_message)

    def on_message(self, msg):
        if msg.kind == 'message':
            self.write_message(str(msg.body))
        if msg.kind == 'disconnect':
            # Do not try to reconnect, just send a message back
            # to the client and close the client connection
            self.write_message('The connection terminated due to a Redis server error.')
            self.close()

    def on_close(self):
        if self.client.subscribed:
            # self.client.unsubscribe('test_channel')
            self.client.disconnect()


application = tornado.web.Application([
    (r'/', MainHandler),
    (r'/msg', NewMessageHandler),
    (r'/track', MessageHandler),
])

if __name__ == '__main__':
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    print('Demo is runing at 0.0.0.0:8888\nQuit the demo with CONTROL-C')
    tornado.ioloop.IOLoop.instance().start()

复制代码

参考地址

tornado 异步客户端tornado-redis的使用「guoqianqian5812」https://blog.csdn.net/guoqianqian5812/article/details/68587921

tornado-redis连接池的使用「guoqianqian5812」原文链接:https://blog.csdn.net/guoqianqian5812/article/details/68560689

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值