searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

RateLimiter限流器的使用和源码分析

2023-11-29 01:24:49
259
0

1. 关于RateLimiter

RateLimiter是Guava库中提供的一个限流器工具类,用于控制对某个资源的访问速率。它可以帮助我们在高并发的场景下,有效地控制系统的资源消耗,避免系统被过多的请求拖垮。在本文中,我们将对RateLimiter的原理、使用方法以及源码进行介绍和分析,以便读者更好地理解和使用这一工具。

2. RateLimiter的原理

RateLimiter的原理基于令牌桶算法,该算法是一种用于控制流量的经典算法。它的基本思想是,系统以恒定的速率往桶里放入令牌,而访问者则以自己需要的速率从桶中取出令牌。当桶中的令牌数量不足时,访问者需要等待,直到桶中有足够的令牌为止。

RateLimiter在实现上,维护了一个双向队列,用于存储未来的令牌发放时间。当一个请求到来时,RateLimiter会根据当前时间和队列中的令牌发放时间,计算出需要等待的时间。如果等待时间为0,则表示可以立即获取令牌;否则,需要等待相应的时间后才能获取令牌。

3. RateLimiter的使用方法

在Guava库中,使用RateLimiter非常简单。首先,我们需要引入Guava库的依赖:

 
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>版本号</version>
</dependency>
 

然后,我们可以通过RateLimiter.create(double permitsPerSecond)方法来创建一个RateLimiter实例,其中permitsPerSecond表示每秒允许的访问速率。例如,我们可以创建一个每秒允许10个访问的RateLimiter:

 
RateLimiter rateLimiter = RateLimiter.create(10.0);
 

接下来,我们可以使用acquire()方法来获取令牌,该方法会根据当前的令牌发放速率进行限流。例如:

 
rateLimiter.acquire();
 

此外,RateLimiter还提供了tryAcquire()方法,用于尝试获取令牌而不阻塞线程。例如:

 
if (rateLimiter.tryAcquire()) {
    // 获取到令牌,执行相应的操作
} else {
    // 未获取到令牌,执行相应的处理
}
 

通过上述方法,我们可以轻松地在代码中实现对某个资源的访问速率控制。

4. 源码分析

接下来,我们将通过源码分析来深入理解RateLimiter的实现原理。在Guava库的源码中,RateLimiter的实现位于com.google.common.util.concurrent包中。

4.1 RateLimiter的构造方法

RateLimiter类的构造方法如下所示:

 
private RateLimiter(SleepingStopwatch stopwatch) {
    this.stopwatch = Preconditions.checkNotNull(stopwatch);
}
 

在构造方法中,RateLimiter接受一个SleepingStopwatch类型的参数stopwatch。SleepingStopwatch是Guava库中的一个工具类,用于计时和等待。

4.2 RateLimiter.create方法

RateLimiter类提供了一个静态工厂方法create,用于创建RateLimiter实例。其源码如下:

 
public static RateLimiter create(double permitsPerSecond) {
    return create(SleepingStopwatch.createFromSystemTimer(), permitsPerSecond);
}

@VisibleForTesting
static RateLimiter create(SleepingStopwatch stopwatch, double permitsPerSecond) {
    RateLimiter rateLimiter = new SmoothRateLimiter.SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
    rateLimiter.setRate(permitsPerSecond);
    return rateLimiter;
}
 

在create方法中,首先通过SleepingStopwatch.createFromSystemTimer()创建了一个SleepingStopwatch实例,然后调用了create方法的重载版本,传入了stopwatch和permitsPerSecond参数。在重载的create方法中,创建了一个SmoothRateLimiter.SmoothBursty实例,并设置了许可的速率。SmoothRateLimiter.SmoothBursty是RateLimiter的内部类,用于实现限流器的具体逻辑。

4.3 SmoothRateLimiter.SmoothBursty类

SmoothRateLimiter.SmoothBursty类是RateLimiter的内部类,用于实现令牌桶算法的具体逻辑。其定义如下:

 
static final class SmoothBursty extends SmoothRateLimiter {
    final double maxBurstSeconds;

    SmoothBursty(SleepingStopwatch stopwatch, double maxBurstSeconds) {
        super(stopwatch);
        this.maxBurstSeconds = maxBurstSeconds;
    }

    @Override
    void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
        double oldMaxPermits = this.maxPermits;
        this.maxPermits = maxBurstSeconds * permitsPerSecond;
        if (oldMaxPermits == Double.POSITIVE_INFINITY) {
            // if we don't special-case this, we would get 0 / 0
            storedPermits = 0.0;
        } else {
            storedPermits = (oldMaxPermits == 0.0)
                    ? 0.0 // initial state
                    : storedPermits * this.maxPermits / oldMaxPermits;
        }
    }
}
 

在SmoothRateLimiter.SmoothBursty类中,我们可以看到它继承自SmoothRateLimiter,并且定义了一个maxBurstSeconds属性,用于表示最大的突发许可数。在doSetRate方法中,它根据传入的permitsPerSecond和stableIntervalMicros参数计算出最大的许可数,并更新了storedPermits属性的值。

4.4 RateLimiter.acquire方法

RateLimiter类提供了acquire方法,用于获取令牌。其源码如下:

 
public double acquire() {
    return acquire(1);
}

public double acquire(int permits) {
    long microsToWait = reserve(permits);
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / TimeUnit.SECONDS.toMicros(1L);
}
 

在acquire方法中,首先调用了reserve方法来预留许可,然后通过stopwatch.sleepMicrosUninterruptibly方法来等待相应的时间。最后返回了等待时间的秒数。

4.5 RateLimiter.tryAcquire方法

RateLimiter类还提供了tryAcquire方法,用于尝试获取令牌而不阻塞线程。其源码如下:

 
public boolean tryAcquire() {
    return tryAcquire(1, 0, TimeUnit.MILLISECONDS);
}

public boolean tryAcquire(int permits) {
    return tryAcquire(permits, 0, TimeUnit.MILLISECONDS);
}

public boolean tryAcquire(long timeout, TimeUnit unit) {
    return tryAcquire(1, timeout, unit);
}

public boolean tryAcquire(int permits, long timeout, TimeUnit unit) {
    long timeoutMicros = Math.max(unit.toMicros(timeout), 0);
    checkPermits(permits);
    long microsToWait;
    synchronized (mutex) {
        long nowMicros = stopwatch.readMicros();
        if (canAcquire(nowMicros, timeoutMicros)) {
            microsToWait = reserveAndGetWaitLength(permits, nowMicros);
        } else {
            return false;
        }
    }
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return true;
}
 

在tryAcquire方法中,根据传入的参数计算了等待的超时时间,并通过canAcquire方法判断是否可以获取许可。如果可以获取许可,则调用reserveAndGetWaitLength方法来预留许可并返回等待的时间。最后通过stopwatch.sleepMicrosUninterruptibly方法来等待相应的时间,并返回true表示获取许可成功。

通过上述源码分析,我们可以深入了解RateLimiter的实现原理和具体逻辑。在实际使用中,我们可以根据业务需求合理地配置RateLimiter的速率,从而达到对资源的有效控制和管理。

5.总结

在本文中,我们详细介绍了Guava库中的RateLimiter限流器,包括其原理、使用方法以及源码分析。通过仔细阅读RateLimiter的源码,我们可以更好地理解其内部实现和工作原理,从而更好地使用和调优这一工具。

RateLimiter作为Guava库中的一个重要工具类,可以帮助我们在高并发的场景下,有效地控制系统的资源消耗,避免系统被过多的请求拖垮。通过合理地配置RateLimiter的速率,我们可以在一定程度上保护系统免受过载的影响,提升系统的稳定性和可靠性。希望本文能够帮助读者更好地理解和使用RateLimiter,从而在实际项目中发挥其作用。

0条评论
作者已关闭评论
梁****健
11文章数
0粉丝数
梁****健
11 文章 | 0 粉丝
原创

RateLimiter限流器的使用和源码分析

2023-11-29 01:24:49
259
0

1. 关于RateLimiter

RateLimiter是Guava库中提供的一个限流器工具类,用于控制对某个资源的访问速率。它可以帮助我们在高并发的场景下,有效地控制系统的资源消耗,避免系统被过多的请求拖垮。在本文中,我们将对RateLimiter的原理、使用方法以及源码进行介绍和分析,以便读者更好地理解和使用这一工具。

2. RateLimiter的原理

RateLimiter的原理基于令牌桶算法,该算法是一种用于控制流量的经典算法。它的基本思想是,系统以恒定的速率往桶里放入令牌,而访问者则以自己需要的速率从桶中取出令牌。当桶中的令牌数量不足时,访问者需要等待,直到桶中有足够的令牌为止。

RateLimiter在实现上,维护了一个双向队列,用于存储未来的令牌发放时间。当一个请求到来时,RateLimiter会根据当前时间和队列中的令牌发放时间,计算出需要等待的时间。如果等待时间为0,则表示可以立即获取令牌;否则,需要等待相应的时间后才能获取令牌。

3. RateLimiter的使用方法

在Guava库中,使用RateLimiter非常简单。首先,我们需要引入Guava库的依赖:

 
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>版本号</version>
</dependency>
 

然后,我们可以通过RateLimiter.create(double permitsPerSecond)方法来创建一个RateLimiter实例,其中permitsPerSecond表示每秒允许的访问速率。例如,我们可以创建一个每秒允许10个访问的RateLimiter:

 
RateLimiter rateLimiter = RateLimiter.create(10.0);
 

接下来,我们可以使用acquire()方法来获取令牌,该方法会根据当前的令牌发放速率进行限流。例如:

 
rateLimiter.acquire();
 

此外,RateLimiter还提供了tryAcquire()方法,用于尝试获取令牌而不阻塞线程。例如:

 
if (rateLimiter.tryAcquire()) {
    // 获取到令牌,执行相应的操作
} else {
    // 未获取到令牌,执行相应的处理
}
 

通过上述方法,我们可以轻松地在代码中实现对某个资源的访问速率控制。

4. 源码分析

接下来,我们将通过源码分析来深入理解RateLimiter的实现原理。在Guava库的源码中,RateLimiter的实现位于com.google.common.util.concurrent包中。

4.1 RateLimiter的构造方法

RateLimiter类的构造方法如下所示:

 
private RateLimiter(SleepingStopwatch stopwatch) {
    this.stopwatch = Preconditions.checkNotNull(stopwatch);
}
 

在构造方法中,RateLimiter接受一个SleepingStopwatch类型的参数stopwatch。SleepingStopwatch是Guava库中的一个工具类,用于计时和等待。

4.2 RateLimiter.create方法

RateLimiter类提供了一个静态工厂方法create,用于创建RateLimiter实例。其源码如下:

 
public static RateLimiter create(double permitsPerSecond) {
    return create(SleepingStopwatch.createFromSystemTimer(), permitsPerSecond);
}

@VisibleForTesting
static RateLimiter create(SleepingStopwatch stopwatch, double permitsPerSecond) {
    RateLimiter rateLimiter = new SmoothRateLimiter.SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
    rateLimiter.setRate(permitsPerSecond);
    return rateLimiter;
}
 

在create方法中,首先通过SleepingStopwatch.createFromSystemTimer()创建了一个SleepingStopwatch实例,然后调用了create方法的重载版本,传入了stopwatch和permitsPerSecond参数。在重载的create方法中,创建了一个SmoothRateLimiter.SmoothBursty实例,并设置了许可的速率。SmoothRateLimiter.SmoothBursty是RateLimiter的内部类,用于实现限流器的具体逻辑。

4.3 SmoothRateLimiter.SmoothBursty类

SmoothRateLimiter.SmoothBursty类是RateLimiter的内部类,用于实现令牌桶算法的具体逻辑。其定义如下:

 
static final class SmoothBursty extends SmoothRateLimiter {
    final double maxBurstSeconds;

    SmoothBursty(SleepingStopwatch stopwatch, double maxBurstSeconds) {
        super(stopwatch);
        this.maxBurstSeconds = maxBurstSeconds;
    }

    @Override
    void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
        double oldMaxPermits = this.maxPermits;
        this.maxPermits = maxBurstSeconds * permitsPerSecond;
        if (oldMaxPermits == Double.POSITIVE_INFINITY) {
            // if we don't special-case this, we would get 0 / 0
            storedPermits = 0.0;
        } else {
            storedPermits = (oldMaxPermits == 0.0)
                    ? 0.0 // initial state
                    : storedPermits * this.maxPermits / oldMaxPermits;
        }
    }
}
 

在SmoothRateLimiter.SmoothBursty类中,我们可以看到它继承自SmoothRateLimiter,并且定义了一个maxBurstSeconds属性,用于表示最大的突发许可数。在doSetRate方法中,它根据传入的permitsPerSecond和stableIntervalMicros参数计算出最大的许可数,并更新了storedPermits属性的值。

4.4 RateLimiter.acquire方法

RateLimiter类提供了acquire方法,用于获取令牌。其源码如下:

 
public double acquire() {
    return acquire(1);
}

public double acquire(int permits) {
    long microsToWait = reserve(permits);
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / TimeUnit.SECONDS.toMicros(1L);
}
 

在acquire方法中,首先调用了reserve方法来预留许可,然后通过stopwatch.sleepMicrosUninterruptibly方法来等待相应的时间。最后返回了等待时间的秒数。

4.5 RateLimiter.tryAcquire方法

RateLimiter类还提供了tryAcquire方法,用于尝试获取令牌而不阻塞线程。其源码如下:

 
public boolean tryAcquire() {
    return tryAcquire(1, 0, TimeUnit.MILLISECONDS);
}

public boolean tryAcquire(int permits) {
    return tryAcquire(permits, 0, TimeUnit.MILLISECONDS);
}

public boolean tryAcquire(long timeout, TimeUnit unit) {
    return tryAcquire(1, timeout, unit);
}

public boolean tryAcquire(int permits, long timeout, TimeUnit unit) {
    long timeoutMicros = Math.max(unit.toMicros(timeout), 0);
    checkPermits(permits);
    long microsToWait;
    synchronized (mutex) {
        long nowMicros = stopwatch.readMicros();
        if (canAcquire(nowMicros, timeoutMicros)) {
            microsToWait = reserveAndGetWaitLength(permits, nowMicros);
        } else {
            return false;
        }
    }
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return true;
}
 

在tryAcquire方法中,根据传入的参数计算了等待的超时时间,并通过canAcquire方法判断是否可以获取许可。如果可以获取许可,则调用reserveAndGetWaitLength方法来预留许可并返回等待的时间。最后通过stopwatch.sleepMicrosUninterruptibly方法来等待相应的时间,并返回true表示获取许可成功。

通过上述源码分析,我们可以深入了解RateLimiter的实现原理和具体逻辑。在实际使用中,我们可以根据业务需求合理地配置RateLimiter的速率,从而达到对资源的有效控制和管理。

5.总结

在本文中,我们详细介绍了Guava库中的RateLimiter限流器,包括其原理、使用方法以及源码分析。通过仔细阅读RateLimiter的源码,我们可以更好地理解其内部实现和工作原理,从而更好地使用和调优这一工具。

RateLimiter作为Guava库中的一个重要工具类,可以帮助我们在高并发的场景下,有效地控制系统的资源消耗,避免系统被过多的请求拖垮。通过合理地配置RateLimiter的速率,我们可以在一定程度上保护系统免受过载的影响,提升系统的稳定性和可靠性。希望本文能够帮助读者更好地理解和使用RateLimiter,从而在实际项目中发挥其作用。

文章来自个人专栏
微服务相关
9 文章 | 1 订阅
0条评论
作者已关闭评论
作者已关闭评论
0
0