Redis使用手册
上QQ阅读APP看书,第一时间看更新

示例:计数器

除了ID生成器之外,计数器也是构建应用程序时必不可少的组件之一,如对于网站的访客数量、用户执行某个操作的次数、某首歌或者某个视频的播放量、论坛帖子的回复数量等,记录这些信息都需要用到计数器。实际上,计数器在互联网中几乎无处不在,因此如何简单、高效地实现计数器一直都是构建应用程序时经常会遇到的一个问题。

代码清单2-7 展示了一个计数器实现,这个程序把计数器的值存储在一个字符串键里面,并通过INCRBY命令和DECRBY命令对计数器的值执行加法操作和减法操作,在需要时,用户还可以通过调用GETSET方法来清零计数器并取得清零之前的旧值。

代码清单2-7 使用字符串键实现的计数器:/string/counter.py

        class Counter:
            def__init__(self, client, key):
                self.client = client
                self.key = key

           defincrease(self, n=1):
                """
                将计数器的值加上n,然后返回计数器当前的值。
                如果用户没有显式地指定n,那么将计数器的值加上1
                """
                returnself.client.incr(self.key, n)

           defdecrease(self, n=1):
                """
                将计数器的值减去n,然后返回计数器当前的值。
                如果用户没有显式地指定n,那么将计数器的值减去1
                """
                returnself.client.decr(self.key, n)

           defget(self):
                """
                返回计数器当前的值
                """
                # 尝试获取计数器当前的值
                value = self.client.get(self.key)
                # 如果计数器并不存在,那么返回0作为计数器的默认值
                ifvalue isNone:
                    return0
                else:
                    # 因为redis-pyget()方法返回的是字符串值,所以这里需要使用int()函数将字
                    # 符串格式的数字转换为真正的数字类型,比如将"10"转换为10
                    returnint(value)

           defreset(self):
                """
                清零计数器,并返回计数器在被清零之前的值
                """
                old_value = self.client.getset(self.key, 0)
                # 如果计数器之前并不存在,那么返回0作为它的旧值
                ifold_value isNone:
                    return0
                else:
                    # redis-pyget()方法一样,getset()方法返回的也是字符串值,所以程序在
                    # 将计数器的旧值返回给调用者之前,需要先将它转换成真正的数字
                    returnint(old_value)

在这个程序中,increase()方法和decrease()方法在定义时都使用了Python的参数默认值特性:

        defincrease(self, n=1):
        defdecrease(self, n=1):

以上定义表明,如果用户直接以无参数的方式调用increase()或者decrease(),那么参数n的值将会被设置为1。

在设置了参数n之后,increase()方法和decrease()方法会分别调用INCRBY命令和DECRBY命令,根据参数n的值,对给定的键执行加法或减法操作:

        # increase()方法
        returnself.client.incr(self.key, n)

       # decrease()方法
        return self.client.decr(self.key, n)

注意,increase()方法在内部调用的是incr()方法而不是incrby()方法,并且decrease()方法在内部调用的也是decr()方法而不是decrby()方法,这是因为在redis-py客户端中,INCR命令和INCRBY命令都是由incr()方法负责执行的:

如果用户在调用incr()方法时没有给定增量,那么incr()方法就默认用户指定的增量为1,并执行INCR命令。

如果用户在调用incr()方法时给定了增量,那么incr()方法就会执行INCRBY命令,并根据给定的增量执行加法操作。

decr()方法的情况也与此类似,只是被调用的命令变成了DECR命令和DECRBY命令。

以下代码展示了这个计数器的使用方法:

        >>> fromredisimportRedis
        >>> fromcounterimportCounter
        >>> client = Redis(decode_responses=True)
        >>> counter = Counter(client, "counter::page_view")
        >>> counter.increase()     # 将计数器的值加上1
        1
        >>> counter.increase()     # 将计数器的值加上1
        2
        >>> counter.increase(10)  # 将计数器的值加上10
        12
        >>> counter.decrease()     # 将计数器的值减去1
        11
        >>> counter.decrease(5)    # 将计数器的值减去5
        6
        >>> counter.reset()        # 重置计数器,并返回旧值
        6
        >>> counter.get()           # 返回计数器当前的值
        0