/* * * Copyright 2016 Robert Winkler and Bohdan Storozhuk * * 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 io.github.resilience4j.ratelimiter; import io.github.resilience4j.ratelimiter.event.RateLimiterEvent; import io.github.resilience4j.ratelimiter.internal.AtomicRateLimiter; import io.reactivex.Flowable; import io.vavr.CheckedFunction0; import io.vavr.CheckedFunction1; import io.vavr.CheckedRunnable; import java.time.Duration; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; /** * A RateLimiter distributes permits at a configurable rate. {@link #getPermission} blocks if necessary * until a permit is available, and then takes it. Once acquired, permits need not be released. */ public interface RateLimiter { /** * Creates a RateLimiter with a custom RateLimiter configuration. * * @param name the name of the RateLimiter * @param rateLimiterConfig a custom RateLimiter configuration * @return The {@link RateLimiter} */ static RateLimiter of(String name, RateLimiterConfig rateLimiterConfig) { return new AtomicRateLimiter(name, rateLimiterConfig); } /** * Creates a RateLimiter with a custom RateLimiterConfig configuration. * * @param name the name of the RateLimiter * @param rateLimiterConfigSupplier a supplier of a custom RateLimiterConfig configuration * @return The {@link RateLimiter} */ static RateLimiter of(String name, Supplier<RateLimiterConfig> rateLimiterConfigSupplier) { return new AtomicRateLimiter(name, rateLimiterConfigSupplier.get()); } /** * Creates a RateLimiter with a default RateLimiterConfig configuration. * * @param name the name of the RateLimiter * @return The {@link RateLimiter} */ static RateLimiter ofDefaults(String name) { return new AtomicRateLimiter(name, RateLimiterConfig.ofDefaults()); } /** * Returns a supplier which is decorated by a rateLimiter. * * @param rateLimiter the rateLimiter * @param supplier the original supplier * @param <T> the type of the returned CompletionStage's result * @return a supplier which is decorated by a RateLimiter. */ static <T> Supplier<CompletionStage<T>> decorateCompletionStage(RateLimiter rateLimiter, Supplier<CompletionStage<T>> supplier) { return () -> { final CompletableFuture<T> promise = new CompletableFuture<>(); try { waitForPermission(rateLimiter); supplier.get() .whenComplete( (result, throwable) -> { if (throwable != null) { promise.completeExceptionally(throwable); } else { promise.complete(result); } } ); } catch (Throwable throwable) { promise.completeExceptionally(throwable); } return promise; }; } /** * Creates a supplier which is restricted by a RateLimiter. * * @param rateLimiter the RateLimiter * @param supplier the original supplier * @param <T> the type of results supplied supplier * @return a supplier which is restricted by a RateLimiter. */ static <T> CheckedFunction0<T> decorateCheckedSupplier(RateLimiter rateLimiter, CheckedFunction0<T> supplier) { return () -> { waitForPermission(rateLimiter); return supplier.apply(); }; } /** * Creates a runnable which is restricted by a RateLimiter. * * @param rateLimiter the RateLimiter * @param runnable the original runnable * @return a runnable which is restricted by a RateLimiter. */ static CheckedRunnable decorateCheckedRunnable(RateLimiter rateLimiter, CheckedRunnable runnable) { return () -> { waitForPermission(rateLimiter); runnable.run(); }; } /** * Creates a function which is restricted by a RateLimiter. * * @param rateLimiter the RateLimiter * @param function the original function * @param <T> the type of function argument * @param <R> the type of function results * @return a function which is restricted by a RateLimiter. */ static <T, R> CheckedFunction1<T, R> decorateCheckedFunction(RateLimiter rateLimiter, CheckedFunction1<T, R> function) { return (T t) -> { waitForPermission(rateLimiter); return function.apply(t); }; } /** * Creates a supplier which is restricted by a RateLimiter. * * @param rateLimiter the RateLimiter * @param supplier the original supplier * @param <T> the type of results supplied supplier * @return a supplier which is restricted by a RateLimiter. */ static <T> Supplier<T> decorateSupplier(RateLimiter rateLimiter, Supplier<T> supplier) { return () -> { waitForPermission(rateLimiter); return supplier.get(); }; } static <T> Callable<T> decorateCallable(RateLimiter rateLimiter, Callable<T> callable) { return () -> { waitForPermission(rateLimiter); return callable.call(); }; } /** * Creates a consumer which is restricted by a RateLimiter. * * @param rateLimiter the RateLimiter * @param consumer the original consumer * @param <T> the type of the input to the consumer * @return a consumer which is restricted by a RateLimiter. */ static <T> Consumer<T> decorateConsumer(RateLimiter rateLimiter, Consumer<T> consumer) { return (T t) -> { waitForPermission(rateLimiter); consumer.accept(t); }; } /** * Creates a runnable which is restricted by a RateLimiter. * * @param rateLimiter the RateLimiter * @param runnable the original runnable * @return a runnable which is restricted by a RateLimiter. */ static Runnable decorateRunnable(RateLimiter rateLimiter, Runnable runnable) { return () -> { waitForPermission(rateLimiter); runnable.run(); }; } /** * Creates a function which is restricted by a RateLimiter. * * @param rateLimiter the RateLimiter * @param function the original function * @param <T> the type of the input to the function * @param <R> the type of the result of the function * @return a function which is restricted by a RateLimiter. */ static <T, R> Function<T, R> decorateFunction(RateLimiter rateLimiter, Function<T, R> function) { return (T t) -> { waitForPermission(rateLimiter); return function.apply(t); }; } /** * Will wait for permission within default timeout duration. * * @param rateLimiter the RateLimiter to get permission from * @throws RequestNotPermitted if waiting time elapsed before a permit was acquired. * @throws IllegalStateException if thread was interrupted during permission wait */ static void waitForPermission(final RateLimiter rateLimiter) throws IllegalStateException, RequestNotPermitted { RateLimiterConfig rateLimiterConfig = rateLimiter.getRateLimiterConfig(); Duration timeoutDuration = rateLimiterConfig.getTimeoutDuration(); boolean permission = rateLimiter.getPermission(timeoutDuration); if (Thread.interrupted()) { throw new IllegalStateException("Thread was interrupted during permission wait"); } if (!permission) { throw new RequestNotPermitted("Request not permitted for limiter: " + rateLimiter.getName()); } } /** * Acquires a permission from this rate limiter, blocking until one is * available. * <p>If the current thread is {@linkplain Thread#interrupt interrupted} * while waiting for a permit then it won't throw {@linkplain InterruptedException}, * but its interrupt status will be set. * * @param timeoutDuration the maximum time to wait * @return {@code true} if a permit was acquired and {@code false} * if waiting timeoutDuration elapsed before a permit was acquired */ boolean getPermission(Duration timeoutDuration); /** * Get the name of this RateLimiter * * @return the name of this RateLimiter */ String getName(); /** * Get the RateLimiterConfig of this RateLimiter. * * @return the RateLimiterConfig of this RateLimiter */ RateLimiterConfig getRateLimiterConfig(); /** * Get the Metrics of this RateLimiter. * * @return the Metrics of this RateLimiter */ Metrics getMetrics(); /** * Returns a reactive stream of RateLimiter. * * @return a reactive stream of RateLimiter */ Flowable<RateLimiterEvent> getEventStream(); /** * Decorates and executes the decorated Supplier. * * @param supplier the original Supplier * @param <T> the type of results supplied by this supplier * @return the result of the decorated Supplier. */ default <T> T executeSupplier(Supplier<T> supplier){ return decorateSupplier(this, supplier).get(); } /** * Decorates and executes the decorated Callable. * * @param callable the original Callable * * @return the result of the decorated Callable. * @param <T> the result type of callable * @throws Exception if unable to compute a result */ default <T> T executeCallable(Callable<T> callable) throws Exception{ return decorateCallable(this, callable).call(); } /** * Decorates and executes the decorated Runnable. * * @param runnable the original Runnable */ default void executeRunnable(Runnable runnable){ decorateRunnable(this, runnable).run(); } interface Metrics { /** * Returns an estimate of the number of threads waiting for permission * in this JVM process. * <p>This method is typically used for debugging and testing purposes. * * @return estimate of the number of threads waiting for permission. */ int getNumberOfWaitingThreads(); /** * Estimates count of available permissions. * Can be negative if some permissions where reserved. * <p>This method is typically used for debugging and testing purposes. * * @return estimated count of permissions */ int getAvailablePermissions(); } }