Java tutorial
/* * Copyright 2014-2015 JKOOL, LLC. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jkoolcloud.tnt4j.limiter; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import com.google.common.util.concurrent.AtomicDouble; import com.google.common.util.concurrent.RateLimiter; /** * Default rate limiter implementation (thread safe) based on Google Guava Library * {@code https://code.google.com/p/guava-libraries/} * * @version $Revision: 1 $ */ public class LimiterImpl implements Limiter { boolean doLimit = false; long start = System.currentTimeMillis(); long idleReset = 0L; // time between limiter accesses before resetting (0 implies no idle reset) AtomicLong byteCount = new AtomicLong(0); AtomicLong msgCount = new AtomicLong(0); AtomicLong delayCount = new AtomicLong(0); AtomicLong denyCount = new AtomicLong(0); AtomicDouble sleepCount = new AtomicDouble(0); AtomicDouble lastSleep = new AtomicDouble(0); AtomicLong lastAccess = new AtomicLong(System.nanoTime()); RateLimiter bpsLimiter = RateLimiter.create(MAX_RATE); RateLimiter mpsLimiter = RateLimiter.create(MAX_RATE); public LimiterImpl(double maxMps, double maxBps, boolean enabled) { setLimits(maxMps, maxBps); setEnabled(enabled); } @Override public long getIdleReset() { return idleReset / (1000L * 1000L); // maintained as nanoseconds, return as msec } @Override public Limiter setIdleReset(long idleReset) { this.idleReset = idleReset * (1000L * 1000L); // input is msec, convert to nanoseconds to compare with nanoTime return this; } @Override public double getMaxMPS() { return mpsLimiter.getRate(); } @Override public double getMaxBPS() { return bpsLimiter.getRate(); } @Override public Limiter setLimits(double maxMps, double maxBps) { mpsLimiter.setRate(maxMps <= 0.0D ? MAX_RATE : maxMps); bpsLimiter.setRate(maxBps <= 0.0D ? MAX_RATE : maxBps); return this; } @Override public double getMPS() { return msgCount.get() * 1000.0 / getAge(); } @Override public double getBPS() { return byteCount.get() * 1000.0 / getAge(); } @Override public Limiter setEnabled(boolean flag) { doLimit = flag; if (doLimit) { reset(); } return this; } @Override public boolean isEnabled() { return doLimit; } @Override public boolean tryObtain(int msgCount, int byteCount) { return tryObtain(msgCount, byteCount, 0, TimeUnit.SECONDS); } @Override public boolean tryObtain(int msgs, int bytes, long timeout, TimeUnit unit) { testIdleReset(); count(msgs, bytes); if (!doLimit || (msgs == 0 && bytes == 0)) { return true; } boolean permit = true; if (bytes > 0) { permit = bpsLimiter.tryAcquire(bytes, timeout, unit); } if (msgs > 0) { permit = permit && mpsLimiter.tryAcquire(msgs, timeout, unit); } if (!permit) { denyCount.incrementAndGet(); } return permit; } @Override public double obtain(int msgs, int bytes) { testIdleReset(); count(msgs, bytes); if (!doLimit || (msgs == 0 && bytes == 0)) { return 0; } double elapsedSecByBps; double elapsedSecByMps; int delayCounter = 0; elapsedSecByBps = bpsLimiter.acquire(bytes); if (elapsedSecByBps > 0) delayCounter++; elapsedSecByMps = mpsLimiter.acquire(msgs); if (elapsedSecByMps > 0) delayCounter++; double sleepTime = elapsedSecByBps + elapsedSecByMps; if (sleepTime > 0) { lastSleep.set(sleepTime); sleepCount.addAndGet(sleepTime); delayCount.addAndGet(delayCounter); } return sleepTime; } protected void count(int msgs, int bytes) { if (bytes > 0) { byteCount.addAndGet(bytes); } if (msgs > 0) { msgCount.addAndGet(msgs); } } protected void testIdleReset() { long accessTime = System.nanoTime(); if (doLimit && idleReset > 0 && (accessTime - lastAccess.get()) > idleReset) { synchronized (this) { // test again in case multiple threads are attempting at same time // and one of the other threads successfully recreated limiters if ((accessTime - lastAccess.get()) > idleReset) { mpsLimiter = RateLimiter.create(mpsLimiter.getRate()); bpsLimiter = RateLimiter.create(bpsLimiter.getRate()); // record this access time so that other threads blocked at // same time don't also recreate limiters lastAccess.set(accessTime); reset(); } } } long prev = lastAccess.getAndSet(accessTime); if (prev > accessTime) lastAccess.set(prev); } @Override public Limiter reset() { byteCount.set(0); msgCount.set(0); sleepCount.set(0); delayCount.set(0); start = System.currentTimeMillis(); return this; } @Override public long getStartTime() { return start; } @Override public long getAge() { return Math.max(System.currentTimeMillis() - start, 1); } @Override public long getTotalBytes() { return byteCount.get(); } @Override public long getTotalMsgs() { return msgCount.get(); } @Override public double getLastDelayTime() { return lastSleep.get(); } @Override public double getTotalDelayTime() { return sleepCount.get(); } @Override public long getDelayCount() { return delayCount.get(); } @Override public long getDenyCount() { return denyCount.get(); } @Override public long getTimeSinceLastAccess() { return (System.nanoTime() - lastAccess.get()) / (1000L * 1000L); // return as msec } }