All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.resilience4j.ratelimiter.configure.RateLimiterAspect Maven / Gradle / Ivy

Go to download

Resilience4j is a lightweight, easy-to-use fault tolerance library designed for Java8 and functional programming

There is a newer version: 2.2.0
Show newest version
/*
 * Copyright 2017 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.configure;

import io.github.resilience4j.core.lang.Nullable;
import io.github.resilience4j.fallback.FallbackDecorators;
import io.github.resilience4j.fallback.FallbackMethod;
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import io.github.resilience4j.spelresolver.SpelResolver;
import io.github.resilience4j.utils.AnnotationExtractor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;

/**
 * This Spring AOP aspect intercepts all methods which are annotated with a {@link RateLimiter}
 * annotation. The aspect will handle methods that return a RxJava2 reactive type, Spring Reactor
 * reactive type, CompletionStage type, or value type.
 * 

* The RateLimiterRegistry is used to retrieve an instance of a RateLimiter for a specific backend. *

* Given a method like this: *


 *     {@literal @}RateLimiter(name = "myService")
 *     public String fancyName(String name) {
 *         return "Sir Captain " + name;
 *     }
 * 
* each time the {@code #fancyName(String)} method is invoked, the method's execution will pass * through a a {@link io.github.resilience4j.ratelimiter.RateLimiter} according to the given * config. *

* The fallbackMethod parameter signature must match either: *

* 1) The method parameter signature on the annotated method or 2) The method parameter signature * with a matching exception type as the last parameter on the annotated method */ @Aspect public class RateLimiterAspect implements Ordered { private static final String RATE_LIMITER_RECEIVED = "Created or retrieved rate limiter '{}' with period: '{}'; limit for period: '{}'; timeout: '{}'; method: '{}'"; private static final Logger logger = LoggerFactory.getLogger(RateLimiterAspect.class); private final RateLimiterRegistry rateLimiterRegistry; private final RateLimiterConfigurationProperties properties; private final @Nullable List rateLimiterAspectExtList; private final FallbackDecorators fallbackDecorators; private final SpelResolver spelResolver; public RateLimiterAspect(RateLimiterRegistry rateLimiterRegistry, RateLimiterConfigurationProperties properties, @Autowired(required = false) List rateLimiterAspectExtList, FallbackDecorators fallbackDecorators, SpelResolver spelResolver) { this.rateLimiterRegistry = rateLimiterRegistry; this.properties = properties; this.rateLimiterAspectExtList = rateLimiterAspectExtList; this.fallbackDecorators = fallbackDecorators; this.spelResolver = spelResolver; } /** * Method used as pointcut * * @param rateLimiter - matched annotation */ @Pointcut(value = "@within(rateLimiter) || @annotation(rateLimiter)", argNames = "rateLimiter") public void matchAnnotatedClassOrMethod(RateLimiter rateLimiter) { // Method used as pointcut } @Around(value = "matchAnnotatedClassOrMethod(rateLimiterAnnotation)", argNames = "proceedingJoinPoint, rateLimiterAnnotation") public Object rateLimiterAroundAdvice(ProceedingJoinPoint proceedingJoinPoint, @Nullable RateLimiter rateLimiterAnnotation) throws Throwable { Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod(); String methodName = method.getDeclaringClass().getName() + "#" + method.getName(); if (rateLimiterAnnotation == null) { rateLimiterAnnotation = getRateLimiterAnnotation(proceedingJoinPoint); } if (rateLimiterAnnotation == null) { //because annotations wasn't found return proceedingJoinPoint.proceed(); } String name = spelResolver.resolve(method, proceedingJoinPoint.getArgs(), rateLimiterAnnotation.name()); io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = getOrCreateRateLimiter( methodName, name); Class returnType = method.getReturnType(); String fallbackMethodValue = spelResolver.resolve(method, proceedingJoinPoint.getArgs(), rateLimiterAnnotation.fallbackMethod()); if (StringUtils.isEmpty(fallbackMethodValue)) { return proceed(proceedingJoinPoint, methodName, returnType, rateLimiter); } FallbackMethod fallbackMethod = FallbackMethod .create(fallbackMethodValue, method, proceedingJoinPoint.getArgs(), proceedingJoinPoint.getTarget()); return fallbackDecorators.decorate(fallbackMethod, () -> proceed(proceedingJoinPoint, methodName, returnType, rateLimiter)).apply(); } private Object proceed(ProceedingJoinPoint proceedingJoinPoint, String methodName, Class returnType, io.github.resilience4j.ratelimiter.RateLimiter rateLimiter) throws Throwable { if (rateLimiterAspectExtList != null && !rateLimiterAspectExtList.isEmpty()) { for (RateLimiterAspectExt rateLimiterAspectExt : rateLimiterAspectExtList) { if (rateLimiterAspectExt.canHandleReturnType(returnType)) { return rateLimiterAspectExt .handle(proceedingJoinPoint, rateLimiter, methodName); } } } if (CompletionStage.class.isAssignableFrom(returnType)) { return handleJoinPointCompletableFuture(proceedingJoinPoint, rateLimiter); } return handleJoinPoint(proceedingJoinPoint, rateLimiter); } private io.github.resilience4j.ratelimiter.RateLimiter getOrCreateRateLimiter(String methodName, String name) { io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = rateLimiterRegistry .rateLimiter(name); if (logger.isDebugEnabled()) { RateLimiterConfig rateLimiterConfig = rateLimiter.getRateLimiterConfig(); logger.debug( RATE_LIMITER_RECEIVED, name, rateLimiterConfig.getLimitRefreshPeriod(), rateLimiterConfig.getLimitForPeriod(), rateLimiterConfig.getTimeoutDuration(), methodName ); } return rateLimiter; } @Nullable private RateLimiter getRateLimiterAnnotation(ProceedingJoinPoint proceedingJoinPoint) { if (proceedingJoinPoint.getTarget() instanceof Proxy) { logger.debug( "The rate limiter annotation is kept on a interface which is acting as a proxy"); return AnnotationExtractor .extractAnnotationFromProxy(proceedingJoinPoint.getTarget(), RateLimiter.class); } else { return AnnotationExtractor .extract(proceedingJoinPoint.getTarget().getClass(), RateLimiter.class); } } private Object handleJoinPoint(ProceedingJoinPoint proceedingJoinPoint, io.github.resilience4j.ratelimiter.RateLimiter rateLimiter) throws Throwable { return rateLimiter.executeCheckedSupplier(proceedingJoinPoint::proceed); } /** * handle the asynchronous completable future flow * * @param proceedingJoinPoint AOPJoinPoint * @param rateLimiter configured rate limiter * @return CompletionStage */ private Object handleJoinPointCompletableFuture(ProceedingJoinPoint proceedingJoinPoint, io.github.resilience4j.ratelimiter.RateLimiter rateLimiter) { return rateLimiter.executeCompletionStage(() -> { try { return (CompletionStage) proceedingJoinPoint.proceed(); } catch (Throwable throwable) { throw new CompletionException(throwable); } }); } @Override public int getOrder() { return properties.getRateLimiterAspectOrder(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy