diff --git a/vjkit/src/main/java/com/vip/vjtools/vjkit/concurrent/RateLimiterUtil.java b/vjkit/src/main/java/com/vip/vjtools/vjkit/concurrent/RateLimiterUtil.java new file mode 100644 index 00000000..94bf40a5 --- /dev/null +++ b/vjkit/src/main/java/com/vip/vjtools/vjkit/concurrent/RateLimiterUtil.java @@ -0,0 +1,80 @@ +package com.vip.vjtools.vjkit.concurrent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import com.google.common.util.concurrent.RateLimiter; + +public class RateLimiterUtil { + + /** + * 一个用来定制RateLimiter的方法。默认装满token + * + * @param permitsPerSecond 每秒允许的请求书,可看成QPS + * @param maxBurstSeconds 最大的突发缓冲时间。用来应对突发流量。Guava的实现默认是1s。permitsPerSecond * maxBurstSeconds的数量,就是闲置时预留的缓冲token数量 + */ + public static RateLimiter create(double permitsPerSecond, double maxBurstSeconds) + throws ReflectiveOperationException { + return create(permitsPerSecond, maxBurstSeconds, true); + } + + /** + * 一个用来定制RateLimiter的方法。 + * + * @param permitsPerSecond 每秒允许的请求书,可看成QPS + * @param maxBurstSeconds 最大的突发缓冲时间。用来应对突发流量。Guava的实现默认是1s。permitsPerSecond * maxBurstSeconds的数量,就是闲置时预留的缓冲token数量 + * @param filledWithToken 是否需要创建时就保留有permitsPerSecond * maxBurstSeconds的token + */ + public static RateLimiter create(double permitsPerSecond, double maxBurstSeconds, boolean filledWithToken) + throws ReflectiveOperationException { + Class sleepingStopwatchClass = Class + .forName("com.google.common.util.concurrent.RateLimiter$SleepingStopwatch"); + Method createStopwatchMethod = sleepingStopwatchClass.getDeclaredMethod("createFromSystemTimer"); + createStopwatchMethod.setAccessible(true); + Object stopwatch = createStopwatchMethod.invoke(null); + + Class burstyRateLimiterClass = Class + .forName("com.google.common.util.concurrent.SmoothRateLimiter$SmoothBursty"); + Constructor burstyRateLimiterConstructor = burstyRateLimiterClass.getDeclaredConstructors()[0]; + burstyRateLimiterConstructor.setAccessible(true); + + // set maxBurstSeconds + RateLimiter rateLimiter = (RateLimiter) burstyRateLimiterConstructor.newInstance(stopwatch, maxBurstSeconds); + rateLimiter.setRate(permitsPerSecond); + + if (filledWithToken) { + // set storedPermits + setField(rateLimiter, "storedPermits", permitsPerSecond * maxBurstSeconds); + } + + return rateLimiter; + } + + private static boolean setField(Object targetObject, String fieldName, Object fieldValue) { + Field field; + try { + field = targetObject.getClass().getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + field = null; + } + Class superClass = targetObject.getClass().getSuperclass(); + while (field == null && superClass != null) { + try { + field = superClass.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + superClass = superClass.getSuperclass(); + } + } + if (field == null) { + return false; + } + field.setAccessible(true); + try { + field.set(targetObject, fieldValue); + return true; + } catch (IllegalAccessException e) { + return false; + } + } +} diff --git a/vjkit/src/test/java/com/vip/vjtools/vjkit/concurrent/RateLimiterUtilTest.java b/vjkit/src/test/java/com/vip/vjtools/vjkit/concurrent/RateLimiterUtilTest.java new file mode 100644 index 00000000..b6fcb969 --- /dev/null +++ b/vjkit/src/test/java/com/vip/vjtools/vjkit/concurrent/RateLimiterUtilTest.java @@ -0,0 +1,21 @@ +package com.vip.vjtools.vjkit.concurrent; + +import java.lang.reflect.Field; + +import org.junit.Assert; +import org.junit.Test; + +import com.google.common.util.concurrent.RateLimiter; + +public class RateLimiterUtilTest { + @Test + public void testCreate() throws Exception { + RateLimiter rateLimiter = RateLimiterUtil.create(20000, 0.1); + + Class superClass = rateLimiter.getClass().getSuperclass(); + Field field = superClass.getDeclaredField("storedPermits"); + field.setAccessible(true); + + Assert.assertEquals(2000, (int) field.getDouble(rateLimiter)); + } +}