<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在高並行系統中,我們通常需要通過各種手段來提供系統的可以用性,例如快取、降級和限流等,本文將針對應用中常用的限流演演算法進行詳細的講解。
限流簡稱流量限速(Rate Limit)是指只允許指定的事件進入系統,超過的部分將被拒絕服務、排隊或等待、降級等處理.
常見的限流方案如下:
固定時間視窗是最常見的限流演演算法之一。其中視窗的概念,對應限流場景當中的限流時間單元。
說明:如上圖場景是每秒鐘限流10次,視窗的大小為1秒,每個方塊代表一個請求,綠色的方塊代表正常的請求,紅色的方法代表被限流的請求,在每秒10次的場景中,從左往右當來看,當進入10個請求後,後面的請求都被會被限流。
優點:
缺點:
視窗切換時無法保證限流值。
固定時間視窗的具體實現,可以採用Redis呼叫lua限流指令碼來實現。
local key = KEYS[1] local count = tonumber(ARGV[1]) local time = tonumber(ARGV[2]) local current = redis.call('get', key) if current and tonumber(current) > count then return tonumber(current) end current = redis.call('incr', key) if tonumber(current) == 1 then redis.call('expire', key, time) end return tonumber(current)
public Long ratelimiter(String key ,int time,int count) throws IOException { Resource resource = new ClassPathResource("ratelimiter.lua"); String redisScript = IOUtils.toString(resource.getInputStream(), StandardCharsets.UTF_8); List<String> keys = Collections.singletonList(key); List<String> args = new ArrayList<>(); args.add(Integer.toString(count)); args.add(Integer.toString(time)); long result = redisTemplate.execute(new RedisCallback<Long>() { @Override public Long doInRedis(RedisConnection connection) throws DataAccessException { Object nativeConnection = connection.getNativeConnection(); if (nativeConnection instanceof Jedis) { return (Long) ((Jedis) nativeConnection).eval(redisScript, keys, args); } return -1l; } }); return result; }
@RequestMapping(value = "/RateLimiter", method = RequestMethod.GET) public String RateLimiter() throws IOException { int time=3; int count=1; String key="redis:ratelimiter"; Long number=redisLockUtil.ratelimiter(key, time, count); logger.info("count:{}",number); Map<String, Object> map =new HashMap<>(); if(number==null || number.intValue()>count) { map.put("code", "-1"); map.put("msg", "存取過於頻繁,請稍候再試"); }else{ map.put("code", "200"); map.put("msg", "存取成功"); } return JSON.toJSONString(map); }
說明:測試為3秒鐘存取1次,超過了次數會提示錯誤。
滑動時間視窗演演算法是對固定時間視窗演演算法的一種改進,在滑動視窗的演演算法中,同樣需要針對當前的請求來動態查詢視窗。但視窗中的每一個元素,都是子視窗。子視窗的概念類似於方案一中的固定視窗,子視窗的大小是可以動態調整的。
說明:比如上圖中的場景是每分鐘限流100次。每一個子視窗的時間維度設定為1秒,那麼一分鐘的視窗有60個子視窗。這樣每當一個請求來了之後,我們去動態計算這個視窗的時候,我們最多需找60次。時間複雜度,從線性變成常數級了,時間的複雜度相對來說也會更低了。
關於滑動時間窗的實現,可以採用sentinel,關於sentinel的使用後續將詳細進行講解。
漏桶演演算法是水先進入到漏桶裡,漏桶再以一定的速率出水,當流入水的數量大於流出水時,多餘的水直接溢位。把水換成請求來看,漏桶相當於伺服器佇列,但請求量大於限流閾值時,多出來的請求就會被拒絕服務。漏桶演演算法使用佇列實現,可以以固定的速率控制流量的存取速度,可以做到流量的平整化處理。
說明:
long timeStamp = System.currentTimeMillis(); //當前時間 long capacity = 1000;// 桶的容量 long rate = 1;//水漏出的速度 long water = 100;//當前水量 public boolean leakyBucket() { //先執行漏水,因為rate是固定的,所以可以認為「時間間隔*rate」即為漏出的水量 long now = System.currentTimeMillis(); water = Math.max(0, water -(now-timeStamp) * rate); timeStamp = now; // 水還未滿,加水 if (water < capacity) { water=water+100; return true; } //水滿,拒絕加水 else { return false; } } @RequestMapping(value="/leakyBucketLimit",method = RequestMethod.GET) public void leakyBucketLimit() { for(int i=0;i<20;i++) { fixedThreadPool.execute(new Runnable() { @Override public void run() { if(leakyBucket()) { logger.info("thread name:"+Thread.currentThread().getName()+" "+sdf.format(new Date())); } else { logger.error("請求頻繁"); } } }); } }
令牌桶演演算法是基於漏桶之上的一種改進版本,在令牌桶中,令牌代表當前系統允許的請求上限,令牌會勻速被放入桶中。當桶滿了之後,新的令牌就會被丟棄
@RequestMapping(value="/ratelimit",method = RequestMethod.GET) public void ratelimit() { //每1s產生0.5個令牌,也就是說介面2s只允許呼叫1次 RateLimiter rateLimiter=RateLimiter.create(0.5,1,TimeUnit.SECONDS); for(int i=0;i<10;i++) { fixedThreadPool.execute(new Runnable() { @Override public void run() { //獲取令牌最大等待10秒 if(rateLimiter.tryAcquire(1,10,TimeUnit.SECONDS)) { logger.info("thread name:"+Thread.currentThread().getName()+" "+sdf.format(new Date())); } else { logger.error("請求頻繁"); } } }); } }
執行結果:
-[pool-1-thread-3] ERROR 請求頻繁
[pool-1-thread-2] ERROR 請求頻繁
[pool-1-thread-1] INFO thread name:pool-1-thread-1 2022-08-07 15:44:00
[pool-1-thread-8] ERROR [] - 請求頻繁
[pool-1-thread-9] ERROR [] - 請求頻繁
[pool-1-thread-10] ERROR [] - 請求頻繁
[pool-1-thread-7] INFO [] - thread name:pool-1-thread-7 2022-08-07 15:44:03
[pool-1-thread-6] INFO [] - thread name:pool-1-thread-6 2022-08-07 15:44:05
[pool-1-thread-5] INFO [] - thread name:pool-1-thread-5 2022-08-07 15:44:07
[pool-1-thread-4] INFO [] - thread name:pool-1-thread-4 2022-08-07 15:44:09
說明:介面限制為每2秒請求一次,10個執行緒需要20s才能處理完,但是rateLimiter.tryAcquire限制了10s內沒有獲取到令牌就丟擲異常,所以結果中會有5個是請求頻繁的。
到此這篇關於Redis常見限流演演算法原理及實現的文章就介紹到這了,更多相關Redis限流演演算法內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45