目录
一、令牌桶算法
1 原理
令牌桶算法会限制请求以基本恒定的速率通过服务端,同时又允许一定的突发流量。
基本原理:维持一个令牌桶,以恒定的速度往桶里添加令牌。请求到来时,如果桶里有足够的令牌,允许请求通过,否则被限流。
![](https://i-blog.csdnimg.cn/blog_migrate/bd143f78e6fb41f3699a6d95d0d2e6aa.png)
可以设置一个请求需要的令牌数,比如1个请求需要5个令牌,那桶中需要又至少5个令牌,才能放行该请求。
桶的容量可以按需要设置,如果桶里令牌满了,再生产的令牌就会丢弃。通过设置这个容量,可以控制允许突发流量的大小,比如桶容量是10,一下过来10笔请求,每个请求需要1个令牌,那这10个请求都会立即放行。
2 实现方式
令牌桶一般有两种实现方式,一种是启动一个线程,以恒定的速度向桶中添加令牌,这种开销比较大;还有一种方式是当请求到来时,计算能够产生的令牌数,再判断能否允许请求通过,google开源的Google Guava RateLimiter就是使用的第二种实现方式。
二、RateLimiter使用方式及原理
RateLimiter有两种不同的限流方式:平滑突发限流(SmoothBursty) 和 平滑预热限流(SmoothWarmingUp)。在限流器初始化时,平滑突发限流按照设定的限流速度生产令牌,平滑预热限流则是以低于设定阈值的速度生产令牌,直到一段时间后才按设定阈值来生产令牌。比如我设定的限流阈值是10/s,则100ms应该生产1个令牌,但平滑预热限流器在初始化时,会以更慢的速度比如50ms才生产1个令牌,直到比如1s之后才开始100ms生产1个令牌,这样保证应用启动时,不会有大量突发流量涌入。
如下方法可以创建一个平滑突发限流器(SmoothBursty):
RateLimiter limiter = RateLimiter.create(10);//阈值是10/s
1. acquire()阻塞等待请求通过
acquire()方法会阻塞等待,直到请求允许通过,并返回等待时间。因为我们设置的阈值是10/s,也就是每0.1s会允许一个请求通过,看打印输出也是间隔基本等于0.1s。另外第一笔请求等待时间是0,也就是立即放行了。
RateLimiter limiter = RateLimiter.create(10);//阈值是10/s
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
// 打印输出:
// 0.0
// 0.09723
// 0.095937
// 0.097646
// 0.098113
acquire()方法可以接收一个int类型的入参,表示该笔请求需要几个令牌,如果没传的话,默认1个令牌。我们试下第一笔请求设置需要10个令牌,发现第一笔请求等待时间还是0,但下笔请求等待时间要将近1s,这是因为平滑突发限流器初始化时允许“提前消费”,即放过第一笔请求,第一笔请求需要的令牌需要在之后的时间补上,所以等待1s后才允许第二笔请求通过。
RateLimiter limiter = RateLimiter.create(10);//阈值是10/s
System.out.println(limiter.acquire(10));//该笔请求需要10个令牌
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
// 打印输出:
// 0.0
// 0.99686
// 0.095173
// 0.096402
// 0.096716
我们看下
com.google.common.util.concurrent.RateLimiter#acquire(int)
public double acquire(int permits) {
//1. 计算等待时间
long microsToWait = reserve(permits);
stopwatch.sleepMicrosUninterruptibly(microsToWait);
//将ms转成s
return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
final long reserve(int permits) {
//2. 检查permits>0
checkPermits(permits);
//3. 加锁
synchronized (mutex()) {
return reserveAndGetWaitLength(permits, stopwatch.readMicros());
}
}
final long reserveAndGetWaitLength(int permits, long nowMicros) {
//4. 计算可以提供令牌的时间
long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
//5. 可以提供令牌的时间 - 当前时间,得到等待时间
return max(momentAvailable - nowMicros, 0);
}
看下如何计算 可以提供令牌的时间 的:
com.google.common.util.concurrent.SmoothRateLimiter#reserveEarliestAvailable
final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
//1. 根据当前时间更新令牌数、‘下次允许请求通过的时间:nextFreeTicketMicros’等
resync(nowMicros);
long returnValue = nextFreeTicketMicros;
//3. 计算还需要生产多少令牌freshPermits
double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
double freshPermits = requiredPermits - storedPermitsToSpend;
//4. 计算产生freshPermits个令牌需要多长时间
long waitMicros =
storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)//用于平滑预热限流时,减慢令牌产生速率,增加生产令牌时间
+ (long) (freshPermits * stableIntervalMicros);//需要生产的令牌数 * 产生令牌速率 = 产生令牌的消耗时间
//5. 更新下次允许请求通过的时间
this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
//6. 更新存储的令牌数
this.storedPermits -= storedPermitsToSpend;
//7. 返回下次允许请求通过的时间
//这里是取的更新前的nextFreeTicketMicros,所以可以看出允许请求提前消费,花费未来的时间产生令牌,让下次请求等待更多的时间
return returnValue;
}
void resync(long nowMicros) {
//2. 如果当前时间大于下次允许请求通过的时间的话,
if (nowMicros > nextFreeTicketMicros) {
//计算这段时间产生的令牌数
//coolDownIntervalMicros()方法计算产生令牌的时间间隔,对于平滑突发限流就是 1000/阈值,比如阈值是10,那产生令牌的时间间隔就是100ms
double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();
//更新令牌数storedPermits,maxPermits就是设置的阈值
storedPermits = min(maxPermits, storedPermits + newPermits);
nextFreeTicketMicros = nowMicros;
}
}
2. tryAcquire()判断请求是否允许通过
tryAcquire()方法会判断请求是否允许通过,返回true or false,看下面的例子,第一笔请求通过,后面请求因为间隔时间太短,都被拒绝通过。
RateLimiter limiter = RateLimiter.create(10);
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
//打印输出:
// true
// false
// false
// false
// false
我们在限流器初始化后先睡眠1s,这时连续几笔请求都通过了,因为1s的时间里限流器已经生成了10个令牌,足以让这几笔请求都通过,所以这也能看到令牌桶算法是允许一定突发流量的,这也是令牌桶和漏桶算法的最大区别,漏桶是不允许突发流量通过的。
RateLimiter limiter = RateLimiter.create(10);
TimeUnit.SECONDS.sleep(1);//睡眠1s
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
System.out.println(limiter.tryAcquire());
//打印输出:
// true
// true
// true
// true
// true
我们看下tryAcquire()源码,核心逻辑跟acquire()方法是一样的:
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) {
long timeoutMicros = max(unit.toMicros(timeout), 0);
checkPermits(permits);
long microsToWait;
synchronized (mutex()) {
long nowMicros = stopwatch.readMicros();
//如果本次请求不允许通过,直接返回false
if (!canAcquire(nowMicros, timeoutMicros)) {
return false;
} else {
//reserveAndGetWaitLength()方法上面讲过了,会返回允许本次请求通过的话,需要的等待时间
microsToWait = reserveAndGetWaitLength(permits, nowMicros);
}
}
//睡眠等待时间
stopwatch.sleepMicrosUninterruptibly(microsToWait);
return true;
}
3.create()初始化
回过头来我们再看下限流器初始化时做了什么:
public abstract class RateLimiter {
public static RateLimiter create(double permitsPerSecond) {
return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer());
}
@VisibleForTesting
static RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) {
//这里的maxBurstSeconds * permitsPerSecond 就是桶中最多存放的令牌数
RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);
rateLimiter.setRate(permitsPerSecond);
return rateLimiter;
}
public final void setRate(double permitsPerSecond) {
checkArgument(
permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive");
synchronized (mutex()) {
doSetRate(permitsPerSecond, stopwatch.readMicros());
}
}
}
final void doSetRate(double permitsPerSecond, long nowMicros) {
//根据当前时间更新令牌数、下次允许请求通过的时间等,上面有讲过这个方法
resync(nowMicros);
//计算产生令牌的时间间隔
double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
this.stableIntervalMicros = stableIntervalMicros;
doSetRate(permitsPerSecond, stableIntervalMicros);
}
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
double oldMaxPermits = this.maxPermits;
maxPermits = maxBurstSeconds * permitsPerSecond;
if (oldMaxPermits == Double.POSITIVE_INFINITY) {
// if we don't special-case this, we would get storedPermits == NaN, below
storedPermits = maxPermits;
} else {
storedPermits =
(oldMaxPermits == 0.0)
? 0.0 // 初始化时,maxPermits是double类型自动赋值0
: storedPermits * maxPermits / oldMaxPermits;
}
}
4.SmoothWarmingUp平滑预热限流
上面讲了平滑突发限流SmoothBursty,下面看下SmoothWarmingUp平滑预热限流是怎么实现的,SmoothWarmingUp实现方式非常地绕,不好理解。
先看下SmoothWarmingUp的使用跟SmoothBursty有何不同:
如下创建了一个SmoothWarmingUp限流器,10标识限流阈值是10/s,1表示预热期长度,后面参数是单位。
RateLimiter rateLimiter = RateLimiter.create(10, 1, TimeUnit.SECONDS);
调acquire()方法测试下每笔请求通过的等待时间,如果是SmoothBursty限流的话,每笔请求通过时间应该都是0.1s,但SmoothWarmingUp限流下前几笔请求等待时间都比较长,但逐渐缩短,但到后面稳定在0.1s,把前几笔不是0.1s的时间加起来恰好是1s左右,就是我们设置的预热期时间长度,也就是说明在预热器内产生令牌的速度比正常要慢,然后速度逐渐加快,过了预热期后速度刚好达到正常速度。
RateLimiter rateLimiter = RateLimiter.create(10, 1, TimeUnit.SECONDS);
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
System.out.println(rateLimiter.acquire());
//打印输出:
// 0.0
// 0.278217
// 0.235424
// 0.195448
// 0.155514
// 0.11602
// 0.099218
// 0.096669
// 0.097302
// 0.099566
// 0.097036
// 0.0948
// 0.098321
看下其实现原理,下面是SmoothWarmingUp源码注释中的一个图,还是比较晦涩难懂,按我的理解,横轴是令牌数,纵轴是产生令牌的时间间隔,
maxPermits:最大令牌数
thresholdPermits:阈值令牌数
stableinterval:稳定时产生令牌的时间间隔
coleinterval:预热期最大的令牌时间间隔
这个曲线要从右往左看,限流器初始化后横轴坐标在maxPermits处,随着请求到来,不断有令牌通过,在横轴上往左走,产生令牌的时间间隔随之变小,直到超过阈值点thresholdPermits后,产生令牌的时间间隔维持在稳定的stableinterval上。
/**
* ^ throttling
* |
* cold + /
* interval | /.
* | / .
* | / . ← "warmup period" is the area of the trapezoid between
* | / . thresholdPermits and maxPermits
* | / .
* | / .
* | / .
* stable +----------/ WARM .
* interval | . UP .
* | . PERIOD.
* | . .
* 0 +----------+-------+--------------→ storedPermits
* 0 thresholdPermits maxPermits
*/
比如上面创建的SmoothWarmingUp限流器,10标识限流阈值是10/s,1表示预热期长度,后面参数是单位。初始化后stableinterval=100000微秒=100ms,即稳定后100ms通过一个令牌,coleinterval=3*stableinterval,maxPermits=10,thresholdPermits=5,计算方式在下面doSetRate()方法中。
void doSetRate(double permitsPerSecond, double stableIntervalMicros) {
double oldMaxPermits = maxPermits;
//coldFactor=3,是初始化时写死的
double coldIntervalMicros = stableIntervalMicros * coldFactor;
thresholdPermits = 0.5 * warmupPeriodMicros / stableIntervalMicros;
maxPermits =
thresholdPermits + 2.0 * warmupPeriodMicros / (stableIntervalMicros + coldIntervalMicros);
slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits - thresholdPermits);
if (oldMaxPermits == Double.POSITIVE_INFINITY) {
storedPermits = 0.0;
} else {
storedPermits =
(oldMaxPermits == 0.0)
? maxPermits // 初始化时,storedPermits为maxPermits
: storedPermits * maxPermits / oldMaxPermits;
}
}
SmoothWarmingUp与SmoothBursty在计算过程中,大部分代码是一样的,最大区别在storedPermitsToWaitTime()方法,在上面讲acquire()方法源码中有看到过,该方法计算花掉桶里的permitsToTake个令牌需要额外等待多久,SmoothBursty限流器直接返回0,SmoothWarmingUp限流器会计算预热期多花费的时间。
//花掉桶里的permitsToTake个令牌需要额外等待多久
long storedPermitsToWaitTime(double storedPermits, double permitsToTake) {
double availablePermitsAboveThreshold = storedPermits - thresholdPermits;
long micros = 0;
// measuring the integral on the right part of the function (the climbing line)
if (availablePermitsAboveThreshold > 0.0) {
double permitsAboveThresholdToTake = min(availablePermitsAboveThreshold, permitsToTake);
// TODO(cpovirk): Figure out a good name for this variable.
double length =
permitsToTime(availablePermitsAboveThreshold)
+ permitsToTime(availablePermitsAboveThreshold - permitsAboveThresholdToTake);
micros = (long) (permitsAboveThresholdToTake * length / 2.0);
permitsToTake -= permitsAboveThresholdToTake;
}
// measuring the integral on the left part of the function (the horizontal line)
micros += (long) (stableIntervalMicros * permitsToTake);
return micros;
}
三、自己实现一个简单的令牌桶
1. 最简单的实现
import lombok.Data;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* @author qiuyu
* @date 2023/7/1 4:13 下午
*/
@Data
public class MyRateLimiter1 {
/**
* 令牌桶容量
*/
private double maxPermits;
/**
* 存储的令牌数
*/
private double storePermits;
/**
* 上次请求的时间,ms
*/
private long lastTimeMill;
/**
* 产生令牌的时间间隔,ms
*/
private double intervelMill;
private MyRateLimiter1() {
}
private static MyRateLimiter1 create(double permitsPerSecond) {
MyRateLimiter1 limiter = new MyRateLimiter1();
limiter.setMaxPermits(permitsPerSecond);
limiter.setIntervelMill(1.0 * 1000 / permitsPerSecond);
limiter.setLastTimeMill(System.currentTimeMillis());
return limiter;
}
/**
* 是否允许请求通过
* @param permits 请求需要的令牌数
* @return
*/
public boolean acquire(double permits) {
long now = System.currentTimeMillis();
if (now > lastTimeMill) {
double newPermits = (now - lastTimeMill) / intervelMill;
this.storePermits = Math.min(maxPermits, this.storePermits + newPermits);
this.lastTimeMill = now;
}
if (this.storePermits >= permits) {
this.storePermits = this.storePermits - permits;
return true;
} else {
return false;
}
}
}
2. 能预消费的实现
上面的实现是最简单的,下面再实现一个能够预消费的。
再回顾下预消费的意思,指的是本次消费的令牌数不够了,先把请求放行,不够的令牌数在未来产生,由下笔请求买单。比如现在桶里有1个令牌,过来1个请求,需要消耗5个令牌,先把该笔请求放行,差的4个令牌在未来的时间去生产,所以下笔请求至少要等待4个令牌的产生时间。那为什么这样设计呢?RateLimiter的解释是为了突发性,假如令牌产生速度是1/s,系统平时很空闲,突然过来1个需要100个令牌的请求,那需要等100s再执行吗?这样是有点浪费资源的,所以直接放行,后面再补。
import com.google.common.base.Stopwatch;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import static java.util.concurrent.TimeUnit.MICROSECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* 令牌桶限流器
*
* @author qiuyu
* @date 2023/6/27 8:08 下午
*/
@Data
@Slf4j
public class MyRateLimiter {
/**
* 下次请求可以通过的时间
*/
private long nextFreeTicketMicros = 0L;
/**
* 当前存储的令牌数
*/
private double storedPermits = 0;
/**
* 桶里的最大令牌数
*/
private double maxPermits;
/**
* 生产令牌的时间间隔
*/
private double intervalMicros;
/**
* 时间表
*/
private Stopwatch stopWatch;
private MyRateLimiter() {
}
public static MyRateLimiter create(double permitsPerSecond) {
check(permitsPerSecond);
MyRateLimiter rateLimiter = new MyRateLimiter();
rateLimiter.setIntervalMicros(SECONDS.toMicros(1L) / permitsPerSecond);
rateLimiter.setMaxPermits(permitsPerSecond);
rateLimiter.setStopWatch(Stopwatch.createStarted());
rateLimiter.setNextFreeTicketMicros(rateLimiter.getStopWatch().elapsed(MICROSECONDS));
return rateLimiter;
}
public double acquire() throws InterruptedException {
return acquire(1);
}
/**
* 放行requiredPermits个令牌,返回等待时间
* @param requiredPermits 需要的令牌数
* @return 等待时间
* @throws InterruptedException
*/
public double acquire(double requiredPermits) throws InterruptedException {
long nowMicros = stopWatch.elapsed(MICROSECONDS);
long waitMicros = getWaitLengthInAcquirePermits(requiredPermits, nowMicros);
MICROSECONDS.sleep(waitMicros);
return 1.0 * waitMicros / SECONDS.toMicros(1L);
}
/**
* 获取允许requiredPermits个令牌通过的话,需要的等待时间
* @param requiredPermits 需要的令牌数
* @param nowMicros 当前时间
* @return 等待时间
*/
private long getWaitLengthInAcquirePermits(double requiredPermits, long nowMicros){
if (nowMicros > nextFreeTicketMicros) {
double newPermits = (nowMicros - nextFreeTicketMicros) / intervalMicros;
storedPermits = Math.min(maxPermits, storedPermits + newPermits);
nextFreeTicketMicros = nowMicros;
}
long waitMicros = nextFreeTicketMicros - nowMicros;
double freshPermits = Math.max(requiredPermits - storedPermits, 0);
nextFreeTicketMicros = nextFreeTicketMicros + (long) (freshPermits * intervalMicros);
storedPermits = Math.max(storedPermits - requiredPermits, 0);
return waitMicros;
}
public boolean tryAcquire() throws InterruptedException {
return tryAcquire(1);
}
/**
* 尝试放行requiredPermits个令牌,返回是否允许放行
* @param requiredPermits 需要的令牌数
* @return 是否允许本次请求通过
* @throws InterruptedException
*/
public boolean tryAcquire(double requiredPermits) throws InterruptedException {
long nowMicros = stopWatch.elapsed(MICROSECONDS);
boolean isAcquire = nowMicros >= nextFreeTicketMicros;
if (isAcquire) {
long waitMicros = getWaitLengthInAcquirePermits(requiredPermits, nowMicros);
MICROSECONDS.sleep(waitMicros);
return true;
}
return false;
}
private static void check(double permitsPerSecond) {
if (permitsPerSecond <= 0.0 || Double.isNaN(permitsPerSecond)) {
throw new RuntimeException("permitsPerSecond is illegal");
}
}
}
测试一下,可以看到acquire()方法输出是符合预期的,每隔0.1s通过一次请求:
public static void main(String[] args) throws InterruptedException {
MyRateLimiter myRateLimiter = MyRateLimiter.create(10);
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
}
//打印输出:
// 0.0
// 0.098591
// 0.095315
// 0.099849
// 0.098357
// 0.099653
把第一笔请求需要令牌数设为10,看到第二笔请求等待了1s才允许通过,也是符合预期的:
public static void main(String[] args) throws InterruptedException {
MyRateLimiter myRateLimiter = MyRateLimiter.create(10);
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
System.out.println(myRateLimiter.acquire());
}
//打印输出:
// 0.0
// 0.998834
// 0.094905
// 0.099425
// 0.099982
// 0.095753
再看下tryAcquire()方法,也是符合预期的:
public static void main(String[] args) throws InterruptedException {
MyRateLimiter myRateLimiter = MyRateLimiter.create(10);
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
}
//打印输出:
// true
// false
// false
// false
// false
public static void main(String[] args) throws InterruptedException {
MyRateLimiter myRateLimiter = MyRateLimiter.create(10);
SECONDS.sleep(1); //睡眠1s
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
System.out.println(myRateLimiter.tryAcquire());
}
//打印输出:
// true
// true
// true
// true
// true
当然这只是最简单的实现,RateLimiter还做了许多其他的事情,比如支持高并发,我们自己写的是有并发问题的;RateLimiter也有很多复杂的算法来提高精度、避免边界问题等;其功能已经经过了广泛测试和使用,是比较稳定可靠的;
举个边界问题的例子,在对时间求和时用了下面方法避免溢出:
public static long saturatedAdd(long a, long b) {
long naiveSum = a + b;
if ((a ^ b) < 0 | (a ^ naiveSum) >= 0) {
// a ^ b:a和b按位异或,相同得0,不同得1,(a ^ b) < 0说明首位不同,即一个正数一个负数,这样的话相加肯定不会溢出
// (a ^ naiveSum) >= 0,这里用(b ^ naiveSum) >= 0判断也一样。说明a(或b)和sum是同为正或同为负,所以不可能是两个大正数或大负数相加,因为两个大正数相加溢出sum是负的,两个大负数相加溢出sum是正的
return naiveSum;
}
//此时已经判断有溢出了,如果sum是负的,说明是两个大正数相加,结果应该是Long.MAX_VALUE;如果sum是正的,说明是两个大负数相加,结果应该是Long.MIN_VALUE
//Long.SIZE是64,naiveSum右移63位,取出最高位,跟1异或相当于取反,得到是1的话跟Long.MAX_VALUE相加溢出得到Long.MIN_VALUE
return Long.MAX_VALUE + ((naiveSum >>> (Long.SIZE - 1)) ^ 1);
}