前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >细品服务并发限流+Redis-cell的使用

细品服务并发限流+Redis-cell的使用

作者头像
袁新栋-jeff.yuan
发布2020-09-01 14:55:51
1K0
发布2020-09-01 14:55:51
举报

背景

今天热搜“海底捞的排号系统挂掉了”,也许是今天情人节,各位情侣去海底捞约会,进入排号系统的流量猛增,导致服务支撑不住,直接挂掉,在这里只是猜测(大胆猜测,小心求证)。那我们应该如何防止因为流量突然猛增而导致服务挂掉的问题呢?那就是限流了。 那我们通过redis 来设计限流策略。

服务限流

简介

  • 通过压测我们可以压出我们服务接口可以承受最大的QPS或者TPS,但是我们压测的话只是单压并不知道在生产环境所能承受的最大流量。如果说其他业务接口也在跑,那这就很难把控这个接口在生产环境可以定多大的QPS或TPS。所以预估某个接口的所能承受的QPS和TPS还是很有水平的。我能力有限今天只聊如何限流。

时间窗口限流

什么是时间窗口?
  • TCP/IP为了提高传输效率(提高吞吐量)采用并发进行传输包,由于有ACk机制,如果等并发发出去的包都回来的话,会影响整体的发送效率,所以只要等到他需要等待的数据就进行发起第二次的传输。有人就会问了,TCP包的传输是有序的,如果并发发送的包,顺序是在后面的包先回来了,那怎么搞,那就继续等待先去的包回来再进行下次操作。 还有就是采用了并发那还得考虑机器的性能,可不能由于发送的包太多导致发送包的服务不可用了。于是就有了滑动时间窗口协议。
  • 具体TCP/IP滑动时间窗口协议详解
  • 滑动时间窗口主要解决的问题就是控制瞬时的量,通过缓存将其分为4个区段,进行滑动处理这4个区段。当这四个区段放满后就会进行等待。想了解得更有深度,可以点击上面链接进行深度学习。下图是TCP滑动窗口示意图
在这里插入图片描述
在这里插入图片描述

时间滑动窗口协议的应用

  1. 现在我们的服务使用的是java语言,现在需要实现一个滑动窗口。 2.使用ReentrantLock(可重入锁)实现,如下图 这样有个问题就是:粒度太大了,不均匀,针对1秒一下的,没法辨析。 我们能不能把粒度拆细了,1秒拆成10个100毫秒。每一个100毫秒有一个计数器。了解TCP/IP的应该知道,TCP/IP为了增加传输速度和控制传输速度,有个叫“滑动窗口协议”。就算拆得再细,也无法解决匀速限制速度的问题。而且还有个临界点问题,比如假如,一秒限制10个请求,在第1秒钟,第2秒 之间,第1秒后半段时间10个请求,第2秒前半段10个请求,那第1秒后半段+第2秒前半段时间组成的一秒钟里就有20个请求,没有起到限速的作用。
在这里插入图片描述
在这里插入图片描述
  1. java中的JUC包中的CyclicBarrier。实现一个限流。我们只允许最多执行多少个线程。如果其中一个阻塞了。那这就很尴尬了。会影像服务的正常的吞吐,但是上面的那种方式,当他阻塞了后,随着时间窗口的推进,会将上一次时间串口的请求的技术进行归零。

漏斗限流

漏斗限流是最常用的限流方法之一,顾名思义,这个算法的灵感源于漏斗(funnel)的结 构。

在这里插入图片描述
在这里插入图片描述
实现一个简单的漏斗算法
package 漏斗算法;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class FunnelRateLimiter {
    static class Funnel {
        //漏斗的容量
        int capacity;
        //速率
        float leakingRate;
        //剩余容量
        int leftQuota;
        long leakingTs;

        public Funnel(int capacity, float leakingRate) {
            this.capacity = capacity;
            this.leakingRate = leakingRate;
            this.leftQuota = capacity;
            this.leakingTs = System.currentTimeMillis();
        }

        void makeSpace() {
            long nowTs = System.currentTimeMillis();
            long deltaTs = nowTs - leakingTs;
            int deltaQuota = (int) (deltaTs * leakingRate);
            // 间隔时间太长,整数数字过大溢出
            if (deltaQuota < 0) {
                this.leftQuota = capacity;
                this.leakingTs = nowTs;
                return;
            }
            // 腾出空间太小,最小单位是 1
            if (deltaQuota < 1) {
                return;
            }
            this.leftQuota += deltaQuota;
            this.leakingTs = nowTs;
            if (this.leftQuota > this.capacity) {
                this.leftQuota = this.capacity;
            }
        }

        boolean watering(int quota) {
            makeSpace();
            if (this.leftQuota >= quota) {
                this.leftQuota -= quota;
                return true;
            }
            return false;
        }
    }

    private Map<String, Funnel> funnels = new ConcurrentHashMap<>();

    public boolean isActionAllowed(String userId, String actionKey, int capacity, float leakingRate) {
        String key = String.format("%s:%s", userId, actionKey);
        Funnel funnel = funnels.get(key);
        if (funnel == null) {
            funnel = new Funnel(capacity, leakingRate);
            funnels.put(key, funnel);
        }
        // 需要 1 个 quota
        return funnel.watering(1);
    }
}
  • 有人就会怀疑自己写的不靠谱,写在服务里面使用内存这更不靠谱,有没有可以使用的中间件,被人造好的轮子。还真有,redis-cell
Redis-cell 的使用
  • Redis 4.0 提供了一个限流 Redis 模块,它叫 redis-cell。该模块也使用了漏斗算法,并 提供了原子的限流指令。有了这个模块,限流问题就非常简单了。
  • 该模块只有 1 条指令 cl.throttle,它的参数和返回值都略显复杂,接下来让我们来看看这 个指令具体该如何使用
在这里插入图片描述
在这里插入图片描述

上面这个指令的意思是允许「用户laoqian回复行为」的频率为每 60s 最多 30 次(漏水速 率),漏斗的初始容量为 15,也就是说一开始可以连续回复 15 个帖子,然后才开始受漏水 速率的影响。我们看到这个指令中漏水速率变成了 2 个参数,替代了之前的单个浮点数。用 两个参数相除的结果来表达漏水速率相对单个浮点数要更加直观一些。

> cl.throttle laoqian:reply 15 30 60
1) (integer) 0 # 0 表示允许,1 表示拒绝
2) (integer) 15 # 漏斗容量 capacity
3) (integer) 14 # 漏斗剩余空间 left_quota
4) (integer) -1 # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
5) (integer) 2 # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒)
  • 在执行限流指令时,如果被拒绝了,就需要丢弃或重试。cl.throttle 指令考虑的非常周 到,连重试时间都帮你算好了,直接取返回结果数组的第四个值进行 sleep 即可,如果不想 阻塞线程,也可以异步定时任务来重试(放入一个队列进行消费队列)。

总结

  • 两种时间限流方法 时间窗口限流和漏斗限流
  • 简单介绍了时间窗口限流在TCP中的应用,TCP为了达到并发,且安全可靠传输采用时间窗口协议进行并发可靠传输包
  • 时间窗口的限流方式不能达到顺滑,为达到顺滑限流采用漏都限流。使用java简单实现漏斗限流
  • Redis4.0 中cell的使用。完美的且简单的就可以实现限流。

参考

  • 《redis 深度历险》
  • https://juejin.im/entry/6844903695432286215
  • https://www.jianshu.com/p/41781605ed29
  • 其他限流算法(令牌桶https://www.google.com/search?q=%E4%BB%A4%E7%89%8C%E6%A1%B6&oq=%E4%BB%A4%E7%89%8C%E6%A1%B6&aqs=chrome…69i57j69i59j0l4.1915j0j7&sourceid=chrome&ie=UTF-8)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-30 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 服务限流
    • 简介
      • 时间窗口限流
        • 什么是时间窗口?
      • 时间滑动窗口协议的应用
        • 漏斗限流
          • 实现一个简单的漏斗算法
          • Redis-cell 的使用
      • 总结
      • 参考
      相关产品与服务
      云数据库 Redis
      腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档