resilience4j 源码解析
ratelimiter模块
@Ratelimiter
Resilience4j
的Ratelimiter
与我们常用的接口限流原理相似,但在实现上考虑了更多的情况,使得限流更加安全可靠。通常我们在Spring中使用@Ratelimiter
注解,因为它天然与Spring集成。下面是其源码及一些解析:
java
@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,
//注意这里可能是null是因为@RateLimiter在类上的情况。
@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();
}
//spring el表达式的解析
String name = spelResolver.resolve(method, proceedingJoinPoint.getArgs(), rateLimiterAnnotation.name());
//获取rateLimiter,核心类
io.github.resilience4j.ratelimiter.RateLimiter rateLimiter = getOrCreateRateLimiter(methodName, name);
Class<?> returnType = method.getReturnType();
//重点,将proceedingJoinPoint进行了增强。返回值CheckedSupplier是仿照Supplier的,你可以看作是一个可调用的函数
final CheckedSupplier<Object> rateLimiterExecution =
() -> proceed(proceedingJoinPoint, methodName, returnType, rateLimiter);
//fallbackMethod增强,执行。
return fallbackExecutor.execute(proceedingJoinPoint, method, rateLimiterAnnotation.fallbackMethod(), rateLimiterExecution);
}
这段代码展示了@Ratelimiter
的切点和环绕通知的定义。关键在于rateLimiterAroundAdvice
方法中对方法的增强,其中使用了RateLimiter
的核心类来实现。
核心方法 - proceed
我们接下看上面的重点proceed方法做了什么
java
private Object proceed(ProceedingJoinPoint proceedingJoinPoint, String methodName,
Class<?> returnType, io.github.resilience4j.ratelimiter.RateLimiter rateLimiter) throws Throwable {
//rateLimiterAspectExtList是用户自定义处理器,由spring管理的bean,体现了resilience4j 的灵活
if (rateLimiterAspectExtList != null && !rateLimiterAspectExtList.isEmpty()) {
for (RateLimiterAspectExt rateLimiterAspectExt : rateLimiterAspectExtList) {
if (rateLimiterAspectExt.canHandleReturnType(returnType)) {
return rateLimiterAspectExt
.handle(proceedingJoinPoint, rateLimiter, methodName);
}
}
}
//CompletionStage是异步任务的顶级接口,如果返回值是异步任务的话执行不一样,在此不讨论。
if (CompletionStage.class.isAssignableFrom(returnType)) {
return handleJoinPointCompletableFuture(proceedingJoinPoint, rateLimiter);
}
//做增强,看下面。
return handleJoinPoint(proceedingJoinPoint, rateLimiter);
}
//handleJoinPoint中有多层嵌套,我们直接看最后的。
static <T> CheckedSupplier<T> decorateCheckedSupplier(RateLimiter rateLimiter, int permits,
CheckedSupplier<T> supplier) {
return () -> {
//等待Permission,后面有解析
waitForPermission(rateLimiter, permits);
try {
//执行proceedingJoinPoint::proceed
T result = supplier.get();
rateLimiter.onResult(result);
//返回
return result;
} catch (Exception exception) {
rateLimiter.onError(exception);
throw exception;
}
};
}
到这里我们就做完了增强。这个增强方法,会作为参数执行最后的语句。我们看看失败后是如何执行fallback的。
首先他会找注解中有没有fallback函数的定义。没有就不增强。有的话就执行
java
fallbackDecorators.decorate(fallbackMethod, primaryFunction).get();
//其中primaryFunction就是我们传进去的方法。官方将这个函数命名为decorate,是借鉴了装饰者模式的思想。
//下面看看怎么执行的
return () -> {
try {
return supplier.get();
} catch (IllegalReturnTypeException e) {
throw e;
} catch (Throwable throwable) {
return fallbackMethod.fallback(throwable);
}
};
//将primaryFunction包了起来,出异常时就执行fallbackMethod。是不是很简单?
这就是@Ratelimiter 的工作原理。下面我们说说他是怎么限流的。
限流实现 - RateLimiter
acquirePermission
方法
RateLimiter
的acquirePermission
方法是关键的限流实现:
java
//AtomicRateLimiter实现
@Override
public boolean acquirePermission(final int permits) {
//超时时间
long timeoutInNanos = state.get().config.getTimeoutDuration().toNanos();
//更新带有退避(back-off)机制的状态的方法
State modifiedState = updateStateWithBackOff(permits, timeoutInNanos);
//等待,返回是否等到了
boolean result = waitForPermissionIfNecessary(timeoutInNanos, modifiedState.nanosToWait);
//发送事件
publishRateLimiterAcquisitionEvent(result, permits);
return result;
}
这段代码展示了如何实现RateLimiter
的核心方法,其中包括了超时处理、状态更新以及等待许可。
其中的事件发布机制 : publishRateLimiterAcquisitionEvent
方法展示了在获取许可后,如何发布事件,这对于监控和日志记录非常重要。
updateStateWithBackOff
方法
java
//如果当前状态 prev 与计算出的下一个状态 next 相同,说明没有其他线程在之前修改过状态,这时就会设置新的状态并退出循环。否 则,继续循环,尝试更新状态。
private State updateStateWithBackOff(final int permits, final long timeoutInNanos) {
AtomicRateLimiter.State prev;
AtomicRateLimiter.State next;
do {
prev = state.get();
//这个方法的核心逻辑是根据当前的活动状态、许可数和超时时长计算下一个状态,确保在退避机制下正确管理许可。
next = calculateNextState(permits, timeoutInNanos, prev);
} while (!compareAndSet(prev, next));
return next;
}
updateStateWithBackOff
方法中,通过自旋方式不断尝试更新RateLimiter
的状态,确保状态的一致性。
等待许可 - waitForPermission
方法
waitForPermissionIfNecessary方法调用了waitForPermission,但是waitForPermissionIfNecessary
方法中,使用了parkNanos
方法进行线程等待,这是一种有效控制线程等待时间的方式,有兴趣可以去看看
waitForPermission
方法展示了如何等待许可的逻辑:
java
private boolean waitForPermission(final long nanosToWait) {
waitingThreads.incrementAndGet();
long deadline = currentNanoTime() + nanosToWait;
boolean wasInterrupted = false;
while (currentNanoTime() < deadline && !wasInterrupted) {
long sleepBlockDuration = deadline - currentNanoTime();
parkNanos(sleepBlockDuration);
wasInterrupted = Thread.interrupted();
}
waitingThreads.decrementAndGet();
if (wasInterrupted) {
currentThread().interrupt();
}
return !wasInterrupted;
}
这段代码展示了在等待许可时的处理,其中使用了类似自旋的方式。
总结
Resilience4j的Ratelimiter模块通过简洁的代码实现了接口限流方法,为单体项目接口限流提供了一种可靠的选择。其实现原理清晰,通过注解和切面的方式,使得在Spring中的集成变得非常便捷。
关注湫湫喵~