優(yōu)點
缺點
controller
@RequestMapping(value = "/start",method = RequestMethod.GET) public Mapstring,object> start(@RequestParam Mapstring, object=""> paramMap) { return testService.startQps(paramMap); }
service
@Override public Mapstring, object=""> startQps(Mapstring, object=""> paramMap) { //根據(jù)前端傳遞的qps上線 Integer times = 100; if (paramMap.containsKey("times")) { times = Integer.valueOf(paramMap.get("times").toString()); } String redisKey = "redisQps"; RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(redisKey, redisTemplate.getConnectionFactory()); int no = redisAtomicInteger.getAndIncrement(); //設(shè)置時間固定時間窗口長度 1S if (no == 0) { redisAtomicInteger.expire(1, TimeUnit.SECONDS); } //判斷是否超限 time=2 表示qps=3 if (no > times) { throw new RuntimeException("qps refuse request"); } //返回成功告知 Mapstring, object=""> map = new HashMap>(); map.put("success", "success"); return map; }
結(jié)果測試
我們設(shè)置的qps=3 , 我們可以看到五個并發(fā)進來后前三個正常訪問,后面兩個就失敗了。稍等一段時間我們在并發(fā)訪問,前三個又可以正常訪問。說明到了下一個時間窗口
優(yōu)點
缺點
controller
@RequestMapping(value = "/startList",method = RequestMethod.GET) public Mapstring,object> startList(@RequestParam Mapstring, object=""> paramMap) { return testService.startList(paramMap); }
service
@RequestMapping(value = "/startList",method = RequestMethod.GET) public Mapstring,object> startList(@RequestParam Mapstring, object=""> paramMap) { return testService.startList(paramMap); }
結(jié)果測試
優(yōu)點
缺點
controller
@RequestMapping(value = "/startLoutong",method = RequestMethod.GET) public Mapstring,object> startLoutong(@RequestParam Mapstring, object=""> paramMap) { return testService.startLoutong(paramMap); }
service
在service中我們通過redis的list的功能模擬出桶的效果。這里代碼是實驗室性質(zhì)的。在真實使用中我們還需要考慮并發(fā)的問題
@Override public Mapstring, object=""> startLoutong(Mapstring, object=""> paramMap) { String redisKey = "qpsList"; Integer times = 100; if (paramMap.containsKey("times")) { times = Integer.valueOf(paramMap.get("times").toString()); } Long size = redisTemplate.opsForList().size(redisKey); if (size >= times) { throw new RuntimeException("qps refuse request"); } Long aLong = redisTemplate.opsForList().rightPush(redisKey, paramMap); if (aLong > times) { //為了防止并發(fā)場景。這里添加完成之后也要驗證。 即使這樣本段代碼在高并發(fā)也有問題。此處演示作用 redisTemplate.opsForList().trim(redisKey, 0, times-1); throw new RuntimeException("qps refuse request"); } Mapstring, object=""> map = new HashMap>(); map.put("success", "success"); return map; }
下游消費
@Component public class SchedulerTask { @Autowired RedisTemplate redisTemplate; private String redisKey="qpsList"; @Scheduled(cron="*/1 * * * * ?") private void process(){ //一次性消費兩個 System.out.println("正在消費。。。。。。"); redisTemplate.opsForList().trim(redisKey, 2, -1); } }
測試
令牌桶和漏桶法是一樣的。只不過將桶的作用方向改變了一下。
漏桶的出水速度是恒定的,如果流量突然增加的話我們就只能拒絕入池
但是令牌桶是將令牌放入桶中,我們知道正常情況下令牌就是一串字符當桶滿了就拒絕令牌的入池,但是面對高流量的時候正常加上我們的超時時間就留下足夠長的時間生產(chǎn)及消費令牌了。這樣就盡可能的不會造成請求的拒絕
最后,不論是對于令牌桶拿不到令牌被拒絕,還是漏桶的水滿了溢出,都是為了保證大部分流量的正常使用,而犧牲掉了少部分流量
public Mapstring, object=""> startLingpaitong(Mapstring, object=""> paramMap) { String redisKey = "lingpaitong"; String token = redisTemplate.opsForList().leftPop(redisKey).toString(); //正常情況需要驗證是否合法,防止篡改 if (StringUtils.isEmpty(token)) { throw new RuntimeException("令牌桶拒絕"); } Mapstring, object=""> map = new HashMap>(); map.put("success", "success"); return map; }
@Scheduled(cron="*/1 * * * * ?") private void process(){ //一次性生產(chǎn)兩個 System.out.println("正在消費。。。。。。"); for (int i = 0; i 2; i++) { redisTemplate.opsForList().rightPush(redisKey, i); } }
到此這篇關(guān)于基于redis實現(xiàn)的四種常見的限流策略的文章就介紹到這了,更多相關(guān)redis 限流策略內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!