/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 gobblin.util.limiter; import java.io.Closeable; import java.util.concurrent.TimeUnit; import com.google.common.primitives.Ints; import com.google.common.util.concurrent.RateLimiter; import com.typesafe.config.Config; import gobblin.annotation.Alias; import lombok.Getter; /** * An implementation of {@link Limiter} that limits the rate of some events. This implementation uses * Guava's {@link RateLimiter}. * * <p> * {@link #acquirePermits(long)} is blocking and will always return {@link true} after the permits * are successfully acquired (probably after being blocked for some amount of time). Permit refills * are not supported in this implementation. Also {@link #acquirePermits(long)} only accepts input * arguments that can be safely casted to an integer bounded by {@link Integer#MAX_VALUE}. * </p> * * @author Yinan Li */ public class RateBasedLimiter extends NonRefillableLimiter { @Alias(value = "qps") public static class Factory implements LimiterFactory { public static final String QPS_KEY = "qps"; @Override public Limiter buildLimiter(Config config) { if (!config.hasPath(QPS_KEY)) { throw new RuntimeException("Missing key " + QPS_KEY); } return new RateBasedLimiter(config.getLong(QPS_KEY)); } } private final RateLimiter rateLimiter; @Getter private double rateLimitPerSecond; public RateBasedLimiter(double rateLimit) { this(rateLimit, TimeUnit.SECONDS); } public RateBasedLimiter(double rateLimit, TimeUnit timeUnit) { this.rateLimitPerSecond = convertRate(rateLimit, timeUnit, TimeUnit.SECONDS); this.rateLimiter = RateLimiter.create(this.rateLimitPerSecond); } @Override public void start() { // Nothing to do } @Override public Closeable acquirePermits(long permits) throws InterruptedException { this.rateLimiter.acquire(Ints.checkedCast(permits)); return NO_OP_CLOSEABLE; } @Override public void stop() { // Nothing to do } private static double convertRate(double originalRate, TimeUnit originalTimeUnit, TimeUnit targetTimeUnit) { return originalRate / targetTimeUnit.convert(1, originalTimeUnit); } }