利用限流工具类RateLimiter实现限流的功能

搭建一个springboot项目

什么是RateLimiter

RateLimiter使用的是一种叫令牌桶的流控算法,RateLimiter会按照一定的频率往桶里扔令牌,线程拿到令牌才能执行,比如你希望自己的应用程序QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里扔1000个令牌
简单点来说, 令牌桶算法会以一定的速率匀速放入令牌; 假设我们设置为1000. 即每秒钟放1000个令牌; 假如现在一秒钟到达了2000个请求, 那么,有1000个请求就会被限流

1. 引入依赖

        
            com.google.guava
            guava
            23.0
        

2. 创建测试Controller


import com.google.common.util.concurrent.RateLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: luobendexiaoqiang
 * @Date: 2020/7/28 17:17
 */
@RestController
public class RateLimiterController {

    private static volatile AtomicInteger atomicInteger = new AtomicInteger();
    private static long startTime = 0;

    public static ConcurrentHashMap resourceRateLimiter = new ConcurrentHashMap<>();

    @PostConstruct
    public void setRate() {
        // 每秒100个令牌
        RateLimiter rateLimiter = RateLimiter.create(100.0);
        resourceRateLimiter.put("order", rateLimiter);
    }


    @GetMapping("/test/rate")
    public void testRate() {
        if (atomicInteger.get() == 0) {
            startTime = System.currentTimeMillis();
            System.out.println("开始时间" + startTime);
        }
        atomicInteger.incrementAndGet();
        RateLimiter rateLimiter = resourceRateLimiter.get("order");
        // 获取令牌
        boolean flag = rateLimiter.tryAcquire();
        //获取到令牌,执行业务逻辑
        if (flag) {
            System.out.println("执行业务逻辑");
            //没获取到令牌, 限流
        } else {
            System.out.println("限流限流");
        }
        if (atomicInteger.get() == 400) {
            System.out.println("消耗时间:" + (System.currentTimeMillis() - startTime) + "ms");
            atomicInteger = new AtomicInteger();
        }
    }


}

3. jmeter创建800个请求,分四秒钟执行完, 即每秒200个请求

利用Jmeter进行接口并发测试

利用限流工具类RateLimiter实现限流的功能_第1张图片
查看结果
利用限流工具类RateLimiter实现限流的功能_第2张图片
因为配置了一百个令牌,即每秒钟只允许100个请求通过, 因此可以看到基本上一半的请求能执行, 一半的执行被限流

其他限流方法

漏桶算法

漏桶算法(Leaky Bucket)是网络世界中流量整形(Traffic Shaping)或速率限制(Rate Limiting)时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。

漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。

漏桶算法和令牌桶算法的选择

漏桶算法与令牌桶算法的区别在于,漏桶算法能够强行限制数据的传输速率,令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。

需要注意的是,在某些情况下,漏桶算法不能够有效地使用网络资源,因为漏桶的漏出速率是固定的,所以即使网络中没有发生拥塞,漏桶算法也不能使某一个单独的数据流达到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。而令牌桶算法则能够满足这些具有突发特性的流量。通常,漏桶算法与令牌桶算法结合起来为网络流量提供更高效的控制。

Java 信号量 Semaphore

Semaphore俗称信用量,是JUC包下一个并发工具类,其实基于AQS实现的共享锁模式,包含非公平锁和公平锁实现. Semaphore可以控制访问的线程数量,可做高并发下限流

模拟Semaphore实现限流功能

public class SemaphoreDemo {

    //定义线程池
    private static ExecutorService executorService = Executors.newCachedThreadPool();

    public static void main(String[] args) {
        //定义一个信号量,限制3个同时执行线程
        Semaphore semaphore = new Semaphore(3);
        //模拟多线程
        for (int i = 0; i < 10; i++) {
            executorService.submit(() -> {
                try {
                    //尝试获取信号量
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+":开始执行");
                    //模拟负责业务操作-休眠2秒
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+":执行完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //释放信号量
                    semaphore.release();
                    System.out.println(Thread.currentThread().getName()+":----------释放");
                }

            });
        }
    }
}


控制台输出

pool-1-thread-1:开始执行 (同时执行)
pool-1-thread-2:开始执行 (同时执行) 
pool-1-thread-3:开始执行 (同时执行)
pool-1-thread-2:执行完成
pool-1-thread-2:----------释放
pool-1-thread-3:执行完成
pool-1-thread-3:----------释放
pool-1-thread-4:开始执行
pool-1-thread-5:开始执行
pool-1-thread-1:执行完成
pool-1-thread-6:开始执行
pool-1-thread-1:----------释放
pool-1-thread-4:执行完成
pool-1-thread-6:执行完成
pool-1-thread-5:执行完成
pool-1-thread-8:开始执行
pool-1-thread-6:----------释放
pool-1-thread-4:----------释放
pool-1-thread-7:开始执行
pool-1-thread-9:开始执行
pool-1-thread-5:----------释放
pool-1-thread-8:执行完成
pool-1-thread-8:----------释放
pool-1-thread-10:开始执行
pool-1-thread-9:执行完成
pool-1-thread-7:执行完成
pool-1-thread-9:----------释放
pool-1-thread-7:----------释放
pool-1-thread-10:执行完成
pool-1-thread-10:----------释放

Semaphore可以控制访问的线程数量, 例如, 我设置他的值为100, 那么, 同一时间只有一百个线程能执行代码逻辑,从而达到限流的目的

你可能感兴趣的:(java)