io.github.resilience4j.bulkhead.configure.BulkheadAspect Maven / Gradle / Ivy
Show all versions of resilience4j-spring Show documentation
/*
* Copyright 2019 lespinsideg
*
* 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.bulkhead.configure;
import io.github.resilience4j.bulkhead.BulkheadFullException;
import io.github.resilience4j.bulkhead.BulkheadRegistry;
import io.github.resilience4j.bulkhead.ThreadPoolBulkhead;
import io.github.resilience4j.bulkhead.ThreadPoolBulkheadRegistry;
import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import io.github.resilience4j.core.lang.Nullable;
import io.github.resilience4j.fallback.FallbackDecorators;
import io.github.resilience4j.fallback.FallbackMethod;
import io.github.resilience4j.spelresolver.SpelResolver;
import io.github.resilience4j.utils.AnnotationExtractor;
import io.vavr.CheckedFunction0;
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.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
/**
* This Spring AOP aspect intercepts all methods which are annotated with a {@link Bulkhead}
* annotation. The aspect will handle methods that return a RxJava2 reactive type, Spring Reactor
* reactive type, CompletionStage type, or value type.
*
* The BulkheadRegistry is used to retrieve an instance of a Bulkhead for a specific name.
*
* Given a method like this:
*
* {@literal @}Bulkhead(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.bulkhead.Bulkhead} 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 BulkheadAspect implements Ordered {
private static final Logger logger = LoggerFactory.getLogger(BulkheadAspect.class);
private final BulkheadConfigurationProperties bulkheadConfigurationProperties;
private final BulkheadRegistry bulkheadRegistry;
private final ThreadPoolBulkheadRegistry threadPoolBulkheadRegistry;
private final @Nullable
List bulkheadAspectExts;
private final FallbackDecorators fallbackDecorators;
private final SpelResolver spelResolver;
public BulkheadAspect(BulkheadConfigurationProperties backendMonitorPropertiesRegistry,
ThreadPoolBulkheadRegistry threadPoolBulkheadRegistry, BulkheadRegistry bulkheadRegistry,
@Autowired(required = false) List bulkheadAspectExts,
FallbackDecorators fallbackDecorators,
SpelResolver spelResolver) {
this.bulkheadConfigurationProperties = backendMonitorPropertiesRegistry;
this.bulkheadRegistry = bulkheadRegistry;
this.bulkheadAspectExts = bulkheadAspectExts;
this.fallbackDecorators = fallbackDecorators;
this.threadPoolBulkheadRegistry = threadPoolBulkheadRegistry;
this.spelResolver = spelResolver;
}
@Pointcut(value = "@within(Bulkhead) || @annotation(Bulkhead)", argNames = "Bulkhead")
public void matchAnnotatedClassOrMethod(Bulkhead Bulkhead) {
}
@Around(value = "matchAnnotatedClassOrMethod(bulkheadAnnotation)", argNames = "proceedingJoinPoint, bulkheadAnnotation")
public Object bulkheadAroundAdvice(ProceedingJoinPoint proceedingJoinPoint,
@Nullable Bulkhead bulkheadAnnotation) throws Throwable {
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
String methodName = method.getDeclaringClass().getName() + "#" + method.getName();
if (bulkheadAnnotation == null) {
bulkheadAnnotation = getBulkheadAnnotation(proceedingJoinPoint);
}
if (bulkheadAnnotation == null) { //because annotations wasn't found
return proceedingJoinPoint.proceed();
}
Class> returnType = method.getReturnType();
String backend = spelResolver.resolve(method, proceedingJoinPoint.getArgs(), bulkheadAnnotation.name());
String fallbackMethodValue = spelResolver.resolve(method, proceedingJoinPoint.getArgs(), bulkheadAnnotation.fallbackMethod());
if (bulkheadAnnotation.type() == Bulkhead.Type.THREADPOOL) {
if (StringUtils.isEmpty(fallbackMethodValue)) {
return proceedInThreadPoolBulkhead(proceedingJoinPoint, methodName, returnType,
backend);
}
return executeFallBack(proceedingJoinPoint, fallbackMethodValue, method,
() -> proceedInThreadPoolBulkhead(proceedingJoinPoint, methodName, returnType,
backend));
} else {
io.github.resilience4j.bulkhead.Bulkhead bulkhead = getOrCreateBulkhead(methodName,
backend);
if (StringUtils.isEmpty(fallbackMethodValue)) {
return proceed(proceedingJoinPoint, methodName, bulkhead, returnType);
}
return executeFallBack(proceedingJoinPoint, fallbackMethodValue, method,
() -> proceed(proceedingJoinPoint, methodName, bulkhead, returnType));
}
}
private Object executeFallBack(ProceedingJoinPoint proceedingJoinPoint, String fallBackMethod,
Method method, CheckedFunction0