Redis深度历险-读书笔记(4)

Redis深度历险-读书笔记(4)

简单限流

限流

  • 由于系统的处理能力是有限的,当外部的请求流量不断增长时,为了保证服务的可用性,需要阻止计划外的请求继续对系统施压。
  • 除了控制流量,限流还有一个用处:控制用户行为,避免垃圾请求。例如社区中的用户,回帖、回复、点赞等行为都要受到控制,如果在规定时间内的某种操作超过了设定的阈值,那这种操作就是非法行为,需要对其进行相应的处理

如何使用Redis来实现简单限流

  • 策略:系统要限定用户的某个行为在指定的时间里只能允许发生 N 次
  • 接口定义:
# 指定用户 user_id 的某个行为 action_key 在特定的时间内 period 只允许发生一定的次数 max_count
def is_action_allowed(user_id, action_key, period, max_count):
    return True
# 调用这个接口 , 一分钟内只允许最多回复 5 个帖子
can_reply = is_action_allowed("user", "reply", 60, 5)
if can_reply:
    do_reply()
else:
    raise ActionThresholdOverflow()
  • 方案
    限流需求需要得到在60秒内用户的行为次数信息,我们通过构造一个滑动时间窗口,获取这段时间内用户的行为次数来判断是否触发限流,窗口的长度60秒,窗口之外的数据都可以忽略。在Redis中,可以使用zsetscore值来构造,用一个 zset 结构记录用户的行为历史,每一个行为都会作为 zset 中的一个 key 保存下来。同一个用户同一种行为用一个 zset 记录,

  • 通过统计滑动窗口内的行为数量与阈值 max_count 进行比较就可以得出当前的行为是否符合限流条件。

  • 为了节省空间,我们只需要保留时间窗口内的行为记录,如果记录为空,那么这个 zset 就可以从内存中移除,不再占用空间。

# coding: utf8

import time
import redis

client = redis.StrictRedis()

def is_action_allowed(user_id, action_key, period, max_count):
    key = 'hist:%s:%s' % (user_id, action_key)
    now_ts = int(time.time() * 1000)  # 毫秒时间戳
    with client.pipeline() as pipe:  # client 是 StrictRedis 实例
        # 记录行为
        pipe.zadd(key, now_ts, now_ts)  # value 和 score 都使用毫秒时间戳
        # 移除时间窗口之前的行为记录,剩下的都是时间窗口内的
        pipe.zremrangebyscore(key, 0, now_ts - period * 1000)
        # 获取窗口内的行为数量
        pipe.zcard(key)
        # 设置 zset 过期时间,避免冷用户持续占用内存
        # 过期时间应该等于时间窗口的长度,再多宽限 1s
        pipe.expire(key, period + 1)
        # 批量执行
        _, _, current_count, _ = pipe.execute()
    # 比较数量是否超标
    return current_count <= max_count


for i in range(20):
    print is_action_allowed("user", "reply", 60, 5)

每一个行为到来时,都维护一次时间窗口。将时间窗口外的记录全部清理掉,只保留窗口内的记录。zset 集合中只有 score 值非常重要,value 值没有特别的意义,只需要保证它是唯一的就可以了。

不足

因为简单限流要记录时间窗口内所有的行为记录,如果这个量很大,比如限定 60s 内操作不得超过 100w 次这样的参数,它这样做会消耗大量的存储空间,是不合适的。

Redis中的Info指令

  • 在使用 Redis 时,通过 Info 指令,可以清晰地知道 Redis 内部一系列运行参数。

  • Info 指令显示的信息非常繁多,分为 9 大块,每个块都有非常多的参数,这 9 个块分别是:

    1. Server 服务器运行的环境参数
    2. Clients 客户端相关信息
    3. Memory 服务器运行内存统计数据
    4. Persistence 持久化信息
    5. Stats 通用统计数据
    6. Replication 主从复制相关信息
    7. CPU CPU 使用情况
    8. Cluster 集群信息
    9. KeySpace 键值对统计数量信息
  • Info 可以一次性获取所有的信息,也可以按块取信息。

# 获取所有信息
> info
# 获取内存相关信息
> info memory
# 获取复制相关信息
> info replication

一些常用指令

  • 查看每秒操作数
# ops_per_sec: operations per second,也就是每秒操作数
> redis-cli info stats |grep ops
instantaneous_ops_per_sec:789

ops 是 789,也就是所有客户端每秒会发送 789 条指令到服务器执行。极限情况下,Redis 可以每秒执行 10w 次指令,CPU 几乎完全榨干。如果 qps 过高,可以考虑通过 monitor 指令快速观察一下究竟是哪些 key 访问比较频繁,从而在相应的业务上进行优化,以减少 IO 次数。

  • 查看连接了多少客户端
> redis-cli info clients
# Clients
connected_clients:124  # 这个就是正在连接的客户端数量
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
  • 查看内存占用
> redis-cli info memory | grep used | grep human
used_memory_human:827.46K # 内存分配器 (jemalloc) 从操作系统分配的内存总量
used_memory_rss_human:3.61M  # 操作系统看到的内存占用 ,top 命令看到的内存
used_memory_peak_human:829.41K  # Redis 内存消耗的峰值
used_memory_lua_human:37.00K # lua 脚本引擎占用的内存大小
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 文章已经放到github上 ,如果对您有帮助 请给个star[https://github.com/qqxuanl...
    尼尔君阅读 2,290评论 0 22
  • 【本教程目录】 1.redis是什么2.redis的作者3.谁在使用redis4.学会安装redis5.学会启动r...
    徐猿猿阅读 1,880评论 0 35
  • NOSQL类型简介键值对:会使用到一个哈希表,表中有一个特定的键和一个指针指向特定的数据,如redis,volde...
    MicoCube阅读 4,060评论 2 27
  • 1.1 资料 ,最好的入门小册子,可以先于一切文档之前看,免费。 作者Antirez的博客,Antirez维护的R...
    JefferyLcm阅读 17,120评论 1 51
  • 时间: 2014-2015客户:游击队游戏 Oseram团队Tobias Mannewitz - 创意指导Chri...
    ZeroBY阅读 536评论 0 0