1 packagecom.hsjry.plutus.customer.web;2
3 importlombok.extern.slf4j.Slf4j;4 importorg.springframework.scheduling.annotation.Scheduled;5 importorg.springframework.stereotype.Component;6 importorg.springframework.web.bind.annotation.RequestMapping;7 importorg.springframework.web.bind.annotation.RestController;8
9 importjava.util.Calendar;10 importjava.util.Date;11 importjava.util.concurrent.LinkedBlockingQueue;12 importjava.util.concurrent.atomic.AtomicInteger;13
14
15 /**
16 * 限流器 每分钟限制请求600次,用于解决推送,每分钟六百次的接口请求次数限制问题17 * @Auther: 屈艳锋18 * @Date: 2019/5/23 15:1219 */
20 @Component21 @RestController22 @Slf4j23 public classCurrentLimiter {24
25 /**
26 * 限流请求次数记录容器,用于记录前一分钟,当前分钟,后一分钟的请求次数27 */
28 static AtomicInteger[] limitRequestCountRecordArray = {new AtomicInteger(0), new AtomicInteger(0), new AtomicInteger(0)};29
30 /**
31 * 限流队列,用于存放超过请求次数限制的请求32 */
33 static LinkedBlockingQueue limitScheduleQueue = new LinkedBlockingQueue(1000);34
35
36 /**
37 * 定时任务,每逢五十秒时,将后一分钟的请求次数初始化为038 */
39 @Scheduled(cron = "0/50 * * * * ?")40 public voidinitNextMiniuteValue() {41
42 //限流容器长度
43 int limitArrayLength =limitRequestCountRecordArray.length;44
45 //获取当前分钟数据所在下标
46 int currentIndex = (int) (System.currentTimeMillis() / 60000) %limitArrayLength;47
48 //获取后一分钟数据所在下标
49 int nextIndex = 0;50 if (currentIndex < limitArrayLength - 1) {51 nextIndex = currentIndex + 1;52 }53
54 //将后一分钟的请求次数初始化为0
55 limitRequestCountRecordArray[nextIndex] = new AtomicInteger(0);56 log.info("执行是定时任务,当前分钟下标为{},请求次数为{}",currentIndex,limitRequestCountRecordArray[currentIndex].get());57 }58
59
60 /**
61 * 测试每分钟六百次限流62 *63 *@return
64 *@throwsInterruptedException65 */
66 @RequestMapping("/test")67 public String testLimiter() throwsInterruptedException {68
69 for (int i = 0; i < 1000; i++) {70
71 //获取当前分钟数数据所在限流容器的下标
72 int currentIndex = (int) (System.currentTimeMillis() / 60000) %limitRequestCountRecordArray.length;73
74 //如果超过阀值,则放入队列
75 if (limitRequestCountRecordArray[currentIndex].get() > 599) {76 limitScheduleQueue.put(i);77 } else{78 //当前分钟数数据加一
79 limitRequestCountRecordArray[currentIndex].incrementAndGet();80 log.info("{}分钟正常调用{}", getMinute(newDate()), i);81 }82
83 }84
85 if (limitScheduleQueue.size() > 0) {86 new Thread(this::limitHandler).start();87 }88 return "";89 }90
91
92 /**
93 * 限流处理94 *95 */
96 voidlimitHandler() {97
98 log.info("=========>当前时间:{}", getMinute(newDate()));99 for (int i = 0; i < limitScheduleQueue.size(); i++) {100
101 //获取当前分钟数数据所在限流容器的下标
102 int currentIndex = (int) (System.currentTimeMillis() / 60000) %limitRequestCountRecordArray.length;103
104 //如果超过阀值,则不处理
105 if (limitRequestCountRecordArray[currentIndex].get() > 599) {106 try{107 log.info("{}分钟已超过限制,休息一会,稍微继续~~~",getMinute(newDate()));108 Thread.sleep(60000);109 } catch(InterruptedException e) {110 }111 } else{112 //当前分钟数数据加一
113 limitRequestCountRecordArray[currentIndex].incrementAndGet();114 log.info("{}分钟正常调用中:{}", getMinute(newDate()), limitScheduleQueue.poll());115 }116
117 }118
119 if (limitScheduleQueue.size() > 0) {120 limitHandler();121 }122 }123
124 /**
125 * 返回当前分钟数126 *127 *@paramdate 日期128 *@return返回分钟129 */
130 public static intgetMinute(Date date) {131 Calendar calendar =Calendar.getInstance();132 calendar.setTime(date);133 returncalendar.get(Calendar.MINUTE);134 }135
136 }