点击上方蓝色字体,关注我们

菜菜哥,有时间吗?

YY妹,什么事?

我最近的任务是做个小的秒杀活动,我怕把后端接口压垮,X总说这可关系到公司的存亡

简单呀,你就做个限流呗

这个没做过呀,菜菜哥,帮妹子写一个呗,事成了,以后有什么要求随便说

那好呀,先把我工资涨一下

那算了,我找别人帮忙吧

跟你开玩笑呢,给哥2个小时时间

谢谢菜菜哥,以后你什么要求我都答应你

好嘞,年轻人就是豪爽

◆◆技术分析◆◆

如果你比较关注现在的技术形式,就会知道微服务现在火的一塌糊涂,当然,事物都有两面性,微服务也不是解决技术,架构等问题的万能钥匙。如果服务化带来的利大于弊,菜菜还是推荐将系统服务化。随着服务化的进程的不断演化,各种概念以及技术随之而来。任何一种方案都是为了解决问题而存在。比如:熔断设计,接口幂等性设计,重试机制设计,还有今天菜菜要说的限流设计,等等这些技术几乎都充斥在每个系统中。

就今天来说的限流,书面意思和作用一致,就是为了限制,通过对并发访问或者请求进行限速或者一个时间窗口内的请求进行限速来保护系统。一旦达到了限制的临界点,可以用拒绝服务、排队、或者等待的方式来保护现有系统,不至于发生雪崩现象。

限流就像做帝都的地铁一般,如果你住在西二旗或者天通苑也许会体会的更深刻一些。我更习惯在技术角度用消费者的角度来阐述,需要限流的一般原因是消费者能力有限,目的为了避免超过消费者能力而出现系统故障。当然也有其他类似的情况也可以用限流来解决。

限流的表现形式上大部分可以分为两大类:

1.  限制消费者数量。也可以说消费的最大能力值。比如:数据库的连接池是侧重的是总的连接数。还有菜菜以前写的线程池,本质上也是限制了消费者的最大消费能力。

2.  可以被消费的请求数量。这里的数量可以是瞬时并发数,也可以是一段时间内的总并发数。菜菜今天要帮YY妹子做的也是这个。

除此之外,限流还有别的表现形式,例如按照网络流量来限流,按照cpu使用率来限流等。按照限流的范围又可以分为分布式限流,应用限流,接口限流等。无论怎么变化,限流都可以用以下图来表示:

◆◆常用技术实现◆◆

令牌桶算法

令牌桶是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌,填满了就丢弃令牌,请求是否被处理要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求。令牌桶允许一定程度突发流量,只要有令牌就可以处理,支持一次拿多个令牌。令牌桶中装的是令牌。

漏桶算法

漏桶一个固定容量的漏桶,按照固定常量速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝。漏桶可以看做是一个具有固定容量、固定流出速率的队列,漏桶限制的是请求的流出速率。漏桶中装的是请求。

计数器

有时我们还会使用计数器来进行限流,主要用来限制一定时间内的总并发数,比如数据库连接池、线程池、秒杀的并发数;计数器限流只要一定时间内的总请求数超过设定的阀值则进行限流,是一种简单粗暴的总数量限流,而不是平均速率限流。

除此之外,其实根据不同的业务场景,还可以出现很多不同的限流算法,但是总的规则只有一条:只要符合当前业务场景的限流策略就是最好的

限流的其他基础知识请百度!!

◆◆优雅解决妹子问题◆◆

回归问题,YY妹子的问题,菜菜不准备用以上所说的几种算法来帮助她。菜菜准备用一个按照时间段限制请求总数的方式来限流。 总体思路是这样:

1.  用一个环形来代表通过的请求容器。

2.  用一个指针指向当前请求所到的位置索引,来判断当前请求时间和当前位置上次请求的时间差,依此来判断是否被限制。

3.  如果请求通过,则当前指针向前移动一个位置,不通过则不移动位置

4.  重复以上步骤 直到永远.......

◆◆用代码说话才是王道◆◆

以下代码不改或者稍微修改可用于生产环境

以下代码的核心思路是这样的:指针当前位置的时间元素和当前时间的差来决定是否允许此次请求,这样通过的请求在时间上表现的比较平滑。

思路远比语言重要,任何语言也可为之,请phper,golanger,javaer 自行实现一遍即可

//限流组件,采用数组做为一个环    class LimitService    {        //当前指针的位置        int currentIndex = 0;        //限制的时间的秒数,即:x秒允许多少请求        int limitTimeSencond = 1;        //请求环的容器数组        DateTime?[] requestRing = null;        //容器改变或者移动指针时候的锁        object objLock = new object();

        public LimitService(int countPerSecond,int  _limitTimeSencond)        {            requestRing = new DateTime?[countPerSecond];            limitTimeSencond= _limitTimeSencond;        }

        //程序是否可以继续        public bool IsContinue()        {            lock (objLock)            {                var currentNode = requestRing[currentIndex];                //如果当前节点的值加上设置的秒 超过当前时间,说明超过限制                if (currentNode != null&& currentNode.Value.AddSeconds(limitTimeSencond) >DateTime.Now)                {                    return false;                }                //当前节点设置为当前时间                requestRing[currentIndex] = DateTime.Now;                //指针移动一个位置                MoveNextIndex(ref currentIndex);            }                        return true;        }        //改变每秒可以通过的请求数        public bool ChangeCountPerSecond(int countPerSecond)        {            lock (objLock)            {                requestRing = new DateTime?[countPerSecond];                currentIndex = 0;            }            return true;        }

        //指针往前移动一个位置        private void MoveNextIndex(ref int currentIndex)        {            if (currentIndex != requestRing.Length - 1)            {                currentIndex = currentIndex + 1;            }            else            {                currentIndex = 0;            }        }    }

测试程序如下:

static  LimitService l = new LimitService(1000, 1);        static void Main(string[] args)        {            int threadCount = 50;            while (threadCount >= 0)            {                Thread t = new Thread(s =>                {                    Limit();                });                t.Start();                threadCount--;            }           

            Console.Read();        }

        static void Limit()        {            int i = 0;            int okCount = 0;            int noCount = 0;            Stopwatch w = new Stopwatch();            w.Start();            while (i < 1000000)            {                var ret = l.IsContinue();                if (ret)                {                    okCount++;                }                else                {                    noCount++;                }                i++;            }            w.Stop();            Console.WriteLine($"共用{w.ElapsedMilliseconds},允许:{okCount},  拦截:{noCount}");        }

测试结果如下:

最大用时15秒,共处理请求1000000*50=50000000 次

并未发生GC操作,内存使用率非常低,每秒处理 300万次+请求 。以上程序修改为10个线程,大约用时4秒之内

如果是强劲的服务器或者线程数较少情况下处理速度将会更快

写在最后

以上代码虽然简单,但是却为限流的核心代码(其实还有优化余地),经过其他封装可以适用于Webapi的filter或其他场景。

妹子问题解决了,要不要让她请我吃个饭呢?

●程序员过关斩将--快速迁移10亿级数据

●程序员修神之路--分布式缓存的一条明路(附代码)

●程序员修仙之路--把用户访问记录优化到极致

互联网之路,菜菜与君一同成长

长按识别二维码关注

号外号外:菜菜的福利来啦这是菜菜一直以来的一个愿望,希望给菜菜的支持者一些福利,福利并不大,却是菜菜的一片心意。今天菜菜自掏腰包,请各位注意查收~

一大波福利正在接近

程序员修神之路--高并发优雅的做限流(有福利)相关推荐

  1. 程序员修神之路--高并发系统设计负载均衡架构

    点击上方"蓝字"关注,酷爽一夏 菜菜哥,上次你给我讲的分库分表策略对我帮助很大 有帮助就好,上次请我的咖啡也很好喝~ 呵呵,不过随着访问量的不断加大,网站我又加了nginx做负载均 ...

  2. 程序员修神之路--高并发下如何缩短响应时间

    点击上方"蓝字"带你去看小星星 菜菜哥,请你看电影呀,但是得帮我一个忙 好呀,看什么? 哥斯拉2:怪兽之王 看过了~ X战警:黑凤凰 看过了 追龙2和黑衣人呢? 都看过了,你说帮什 ...

  3. 程序员修神之路--高并发下为什么更喜欢进程内缓存

    菜菜哥,告诉你一个好消息 YY妹子,什么好消息,你有男票了? 不是啦,我做的一个网站,以前经常由于访问量太大而崩溃,现在我加上了缓存,很稳定啦 加的什么缓存呢? 我用的redis,号称业界最快的缓存组 ...

  4. 程序员修神之路--用NOSql给高并发系统加速

    领取福利 记得长按,领取技术书籍哦 随着互联网大潮的到来,越来越多网站,应用系统需要海量数据的支撑,高并发.低延迟.高可用.高扩展等要求在传统的关系型数据库中已经得不到满足,或者说关系型数据库应对这些 ...

  5. 程序员修神之路--做好分库分表其实很难之二

    菜菜哥,上次听你给我讲了分库的情况后,我明白了很多,能再给我讲讲分表吗 有收获就好,分表其实有很多情况和分库类似 还有不一样的情况吗? 有呀,本来数据库和表是不同层面的东西,肯定有差异 那你给讲讲呗 ...

  6. 程序员修神之路--做好分库分表其实很难之一

    点击上方"蓝字"带你去看小星星 菜哥,领导让我开发新系统了 这么说领导对你还是挺信任的呀~ 必须的,为了设计好这个新系统,数据库设计我花了好多心思呢 做一个系统我觉得不应该从数据库 ...

  7. 程序员修神之路--redis做分布式锁可能不那么简单

    点击上方"蓝字"带你去看小星星 菜菜哥,复联四上映了,要不要一起去看看? 又想骗我电影票,对不对? 呵呵,想去看了叫我呀 看来你工作不饱和呀 哪有,这两天我刚基于redis写了一个 ...

  8. 程序员修神之路--问世间异步为何物?

    菜菜哥,今天天气挺热的,我都穿裙子了 说吧,什么事?? 苦笑一下..... 老大说把所有的接口都改成异步操作 异步好呀,最少比同步能提高吞吐量 异步是怎么回事呢,能讲讲不? 来,凑近一点,哥给你解释一 ...

  9. 程序员修神之路--提高网站的吞吐量

    点击上方蓝色字体,关注我们 菜菜哥,有个事你还得帮我呀 呦西,YY妹子,最近天这么热了,你怎么还穿这么多? 苦笑一下.....前几天写了几个接口,领导让提高一下接口吞吐量 这是你技术提高的大好机会呀 ...

最新文章

  1. JsonObject json字符串转换成JSonObject对象
  2. 深入浅析zookeeper的一致性模型及其实现
  3. 如何删除sublime目录
  4. 设计模式之单例设计模式(饿汉式)
  5. python服务器稳定性,一种基于Python服务器稳定性测试的方法技术
  6. 服务器系统的王者——Linux 系统
  7. 华为鲲鹏弹性云服务器KM1_#化鲲为鹏,我有话说# 鲲鹏弹性云服务器配置 Tomcat...
  8. leetcode 174. Dungeon Game | 174. 地下城游戏(暴力递归->傻缓存->dp)
  9. loadrunner 只能并发50_loadrunner 场景设计-(一)
  10. protobuf java 生成_protobuf代码生成
  11. 手把手教你启动若依微服务项目
  12. [Android]解决ClickableSpan中点击后ListView中item的长按冲突的问题
  13. python老师 课时费_花10分钟写一个Python脚本,搞定了初中老师一下午的工作
  14. Atititi 计算机系 教材 目录 1. 硬件类 2 1.1. 《微机系统与接口技术》 2 1.2. 《计算机组成与系统结构(第2版)》 2 2. Atitit 操作系统原理 操作系统原理(cpu
  15. Atitit.加密算法ati Aes的框架设计v2.2
  16. Spring IOC源码笔记(三)
  17. php导出excel代码,php导出excel的实例代码
  18. ubuntu freeradius mysql_ubuntu上安装和配置FreeRadius
  19. Label free定量蛋白质组学
  20. 上海电力大学计算机专业全国排名,上海电力大学有哪些专业 上海电力大学专业排名...

热门文章

  1. “爱思助手”曝为iOS木马:可绕过苹果DRM机制
  2. 2017-03-10Git版本回退
  3. 测试你的Python 水平----6
  4. android键盘弹出,聊天背景不变形
  5. SSH项目中遇到拦截器无法注入服务的问题
  6. 添加CSS的四种方式
  7. 封装log4cp p
  8. windows php的Memcache安装和使用方法
  9. 昨日搬至办公室的书籍
  10. Outlook 阅读窗格(Reading Pane)