All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.yueshutong.springbootstartercurrentlimiting.core.RateLimiterCloud Maven / Gradle / Ivy

package cn.yueshutong.springbootstartercurrentlimiting.core;

import cn.yueshutong.springbootstartercurrentlimiting.common.SpringContextUtil;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static cn.yueshutong.springbootstartercurrentlimiting.common.RedisLockUtil.*;

/**
 * 令牌桶算法:分布式、Redis
 */
public class RateLimiterCloud implements RateLimiter {
    private long size; //令牌桶容量
    private long period; //间隔时间:纳秒
    private long initialDelay; //延迟生效时间:毫秒
    private final int LOCK_GET_EXPIRES = 10 * 1000; //锁过期时间:毫秒
    private final int LOCK_PUT_EXPIRES = 10 * 1000; //实例过期时间:毫秒
    private String LOCK_GET; // 读锁
    private String LOCK_PUT; // 写锁
    private String BUCKET; //令牌桶标识
    private String LOCK_PUT_DATA; //记录上一次操作的时间
    private final String AppCode = SpringContextUtil.getApplicationName() + SpringContextUtil.getPort() + this.hashCode(); //唯一实例标识
    private StringRedisTemplate template = SpringContextUtil.getBean(StringRedisTemplate.class); //获取RedisTemplate

    /**
     * @param QPS          每秒并发量,等于0 默认禁止访问
     * @param initialDelay 首次延迟时间:毫秒
     * @param overflow     是否严格控制请求速率和次数
     */
    private RateLimiterCloud(double QPS, long initialDelay, String bucket, boolean overflow) {
        this.size = overflow ? 1 : (QPS < 1 ? 1 : new Double(QPS).longValue());
        this.initialDelay = initialDelay * 1000 * 1000; //毫秒转纳秒
        this.period = QPS != 0 ? new Double(1000 * 1000 * 1000 / QPS).longValue() : Integer.MAX_VALUE;
        init(bucket);
        if (QPS != 0) { //等于0就不放令牌了
            putScheduled();
        }
    }

    private void init(String bucket) {
        this.BUCKET = bucket;
        this.LOCK_GET = bucket + "$GET";
        this.LOCK_PUT = bucket + "$PUT";
        this.LOCK_PUT_DATA = this.LOCK_PUT + "$DATA";
        template.opsForValue().set(BUCKET, String.valueOf(0)); //初始化令牌桶为0
    }


    public static RateLimiter of(double QPS, long initialDelay, String bucket, boolean overflow) {
        return new RateLimiterCloud(QPS, initialDelay, bucket, overflow);
    }

    /**
     * 获取令牌,阻塞直到成功
     */
    @Override
    public boolean tryAcquire() {
        tryLock(template, LOCK_GET, LOCK_GET, LOCK_GET_EXPIRES, TimeUnit.MILLISECONDS); //取到锁
        try {
            Long s = Long.valueOf(template.opsForValue().get(BUCKET));
            while (s <= 0) { //阻塞
                s = Long.valueOf(template.opsForValue().get(BUCKET));
            }
            template.opsForValue().decrement(BUCKET); //拿走令牌
            return true;
        } finally {
            releaseLock(template, LOCK_GET); //释放锁
        }
    }

    /**
     * 获取令牌,没有令牌立即失败
     */
    @Override
    public boolean tryAcquireFailed() {
        tryLock(template, LOCK_GET, LOCK_GET, LOCK_GET_EXPIRES, TimeUnit.MILLISECONDS); //取到锁
        try {
            Long s = Long.valueOf(template.opsForValue().get(BUCKET));
            if (s > 0) {
                template.opsForValue().decrement(BUCKET); //拿走令牌
                return true;
            }
            return false;
        } finally {
            releaseLock(template, LOCK_GET); //释放锁
        }
    }

    /**
     * 周期性放令牌,控制访问速率
     * 算法:通过抢占机制选举leader,其它候选者对leader进行监督,发现leader懈怠即可将其踢下台。由此进入新一轮的抢占...
     */
    private void putScheduled() {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if (tryLockFailed(template, LOCK_PUT, AppCode) || AppCode.equals(template.opsForValue().get(LOCK_PUT))) { //成为leader
                    Long s = Long.valueOf(template.opsForValue().get(BUCKET));
                    if (size > s) {
                        template.opsForValue().increment(BUCKET);
                    }
                    template.opsForValue().set(LOCK_PUT_DATA, String.valueOf(System.currentTimeMillis()));//更新时间
                } else { //成为候选者
                    Long s = Long.valueOf(template.opsForValue().get(LOCK_PUT_DATA));
                    if (System.currentTimeMillis() - s > LOCK_PUT_EXPIRES) {
                        releaseLock(template, LOCK_PUT); //释放锁
                    }
                }
            }
        }, initialDelay, period, TimeUnit.NANOSECONDS);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy