游戏开发工具

Redis计数器实例

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


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


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

class Counter:

    def __init__(self, client, key):
        self.client = client
        self.key = key

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

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

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

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


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

def increase(self, n=1):
def decrease(self, n=1):


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


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

# increase()方法
return self.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命令。


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

>>> from redis import Redis
>>> from counter import Counter
>>> 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