自定义限流

package com.atguigu.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * 限流器 每分钟限制请求600次,用于解决推送,每分钟六百次的接口请求次数限制问题
 *
 * @Auther: 屈艳锋
 * @Date: 2019/5/23 15:12
 */
@RestController
@Slf4j
public class CurrentLimiter {

    /**
     * 限流请求次数记录容器,用于记录前一分钟,当前分钟,后一分钟的请求次数
     */
    static AtomicInteger[] limitRequestCountRecordArray = {new AtomicInteger(0), new AtomicInteger(0), new AtomicInteger(0)};

    /**
     * 限流队列,用于存放超过请求次数限制的请求
     */
    static LinkedBlockingQueue limitScheduleQueue = new LinkedBlockingQueue<Integer>(4);


    /**
     * 定时任务,每逢五十秒时,将后一分钟的请求次数初始化为0
     */
    @Scheduled(cron = "0/50 * * * * ?")
    public void initNextMiniuteValue() {

        //限流容器长度
        int limitArrayLength = limitRequestCountRecordArray.length;

        //获取当前分钟数据所在下标
        int currentIndex = (int) (System.currentTimeMillis() / 60000) % limitArrayLength;

        //获取后一分钟数据所在下标
        int nextIndex = 0;
        if (currentIndex < limitArrayLength - 1) {
            nextIndex = currentIndex + 1;
        }

        //将后一分钟的请求次数初始化为0
        limitRequestCountRecordArray[nextIndex] = new AtomicInteger(0);
        log.info("执行是定时任务,当前分钟下标为{},请求次数为{}", currentIndex, limitRequestCountRecordArray[currentIndex].get());
    }


    /**
     * 测试每分钟六百次限流
     *
     * @return
     * @throws InterruptedException
     */
    @RequestMapping("/ratelimit/test")
    public String testLimiter() throws InterruptedException {

        for (int i = 0; i < 1000; i++) {

            //获取当前分钟数数据所在限流容器的下标
            int currentIndex = (int) (System.currentTimeMillis() / 60000) % limitRequestCountRecordArray.length;

            //如果超过阀值,则放入队列
            if (limitRequestCountRecordArray[currentIndex].get() > 599) {
                limitScheduleQueue.put(i);
            } else {
                //当前分钟数数据加一
                limitRequestCountRecordArray[currentIndex].incrementAndGet();
                log.info("{}分钟正常调用{}", getMinute(new Date()), i);
            }

        }

        if (limitScheduleQueue.size() > 0) {
            new Thread(this::limitHandler).start();
        }
        return "";
    }


    /**
     * 限流处理
     * 95
     */
    void limitHandler() {

        log.info("=========>当前时间:{}", getMinute(new Date()));
        for (int i = 0; i < limitScheduleQueue.size(); i++) {

            //获取当前分钟数数据所在限流容器的下标
            int currentIndex = (int) (System.currentTimeMillis() / 60000) % limitRequestCountRecordArray.length;

            //如果超过阀值,则不处理
            if (limitRequestCountRecordArray[currentIndex].get() > 599) {
                try {
                    log.info("{}分钟已超过限制,休息一会,稍微继续~~~", getMinute(new Date()));
                    Thread.sleep(60000);
                } catch (InterruptedException e) {
                }
            } else {
                //当前分钟数数据加一
                limitRequestCountRecordArray[currentIndex].incrementAndGet();
                log.info("{}分钟正常调用中:{}", getMinute(new Date()), limitScheduleQueue.poll());
            }
        }

        if (limitScheduleQueue.size() > 0) {
            limitHandler();
        }
    }

    /**
     * 返回当前分钟数
     *
     * @param date 日期
     * @return 返回分钟
     */
    public static int getMinute(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.MINUTE);
    }

}

 摘自https://www.cnblogs.com/quyanfengalan/p/10954972.html

 

posted @ 2021-03-06 12:12  *乐途*  阅读(99)  评论(0)    收藏  举报