resilience4j 源码解析 ratelimiter模块

resilience4j 源码解析

ratelimiter模块

@Ratelimiter

Resilience4jRatelimiter与我们常用的接口限流原理相似,但在实现上考虑了更多的情况,使得限流更加安全可靠。通常我们在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 方法

RateLimiteracquirePermission方法是关键的限流实现:

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中的集成变得非常便捷。

关注湫湫喵~

相关推荐
你的人类朋友31 分钟前
认识一下Bcrypt哈希算法
后端·安全·程序员
tangweiguo030519871 小时前
基于 Django 与 Bootstrap 构建的现代化设备管理平台
后端·django·bootstrap
IT果果日记1 小时前
详解DataX开发达梦数据库插件
大数据·数据库·后端
dazhong20121 小时前
Spring Boot 项目新增 Module 完整指南
java·spring boot·后端
bobz9651 小时前
Cilium + Kubevirt 与 Kube-OVN + Kubevirt 在公有云场景下的对比与选择
后端
David爱编程2 小时前
深度解析:synchronized 性能演进史,从 JDK1.6 到 JDK17
java·后端
脑子慢且灵3 小时前
【JavaWeb】一个简单的Web浏览服务程序
java·前端·后端·servlet·tomcat·web·javaee
用户298698530143 小时前
如何在 C# 中用表格替换 Word 文档中的文本?
后端
山东小木3 小时前
JBoltAI需求分析大师:基于SpringBoot的大模型智能需求文档生成解决方案
人工智能·spring boot·后端·需求分析·jboltai·javaai·aigs
Moonbit3 小时前
MoonBit 再次走进清华:张宏波受邀参加「思源计划」与「程序设计训练课」
前端·后端·编程语言