使用Spring AOP和番石榴速率限制器的节气门方法

外部服务或API可能有使用限制,或者它们不能失败就无法处理大量请求。 这篇文章解释了如何创建一个基于Spring Framework的方面,该方面可以用来限制使用Guava速率限制器的任何建议方法调用。 以下实现需要Java 8,Spring AOP和Guava。

让我们从注释开始,该注释用于建议任何启用Spring AOP的方法调用。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {/*** @return rate limit in queries per second*/int value();/*** @return rate limiter identifier (optional)*/String key() default "";}

注释定义了两件事:每秒查询(或许可)中的速率限制,以及标识速率限制器的可选键。 如果密钥相等,则多种方法可以使用相同的速率限制器。 例如,当使用来自不同方法的不同参数调用API时,每秒所需的总查询次数将不会超过。

接下来是实际的节流方面,它是作为Spring Framework组件实现的。 无论有没有Spring框架,在任何情况下都可以使用方面。

@Aspect
@Component
public class RateLimiterAspect {public interface KeyFactory {String createKey(JoinPoint jp, RateLimit limit);}private static final Logger LOGGER = LoggerFactory.getLogger(RateLimiterAspect.class);private static final KeyFactory DEFAULT_KEY_FACTORY = (jp, limit) -> JoinPointToStringHelper.toString(jp);private final ConcurrentHashMap<String, RateLimiter> limiters;private final KeyFactory keyFactory;@Autowiredpublic RateLimiterAspect(Optional<KeyFactory> keyFactory) {this.limiters = new ConcurrentHashMap<>();this.keyFactory = keyFactory.orElse(DEFAULT_KEY_FACTORY);}@Before("@annotation(limit)")public void rateLimit(JoinPoint jp, RateLimit limit) {String key = createKey(jp, limit);RateLimiter limiter = limiters.computeIfAbsent(key, createLimiter(limit));double delay = limiter.acquire();LOGGER.debug("Acquired rate limit permission ({} qps) for {} in {} seconds", limiter.getRate(), key, delay);}private Function<String, RateLimiter> createLimiter(RateLimit limit) {return name -> RateLimiter.create(limit.value());}private String createKey(JoinPoint jp, RateLimit limit) {return Optional.ofNullable(Strings.emptyToNull(limit.key())).orElseGet(() -> keyFactory.createKey(jp, limit));}
}

该类定义了密钥工厂的附加接口和默认实现,如果注释未为速率限制器提供显式密钥,则使用该默认工厂。 密钥工厂可以使用连接点(基本上是方法调用)和提供的注释为速率限制器创建合适的密钥。 该方面还使用并发哈希图来存储速率限制器实例。 方面被定义为单例,但是可以从多个线程调用rateLimit方法,因此并发哈希图确保我们为每个唯一键仅分配单个速率限制器。 方面的构造器注入利用了Spring Framework的可选注入支持。 如果在上下文中未定义KeyFactory bean,则使用默认的密钥工厂。

该类使用@Aspect和@Component进行注释,以便Spring理解已定义的方面并启用@Before建议。 @Before通知仅包含一个切入点,该切入点需要RateLimit批注并将其绑定到方法的limit参数。 节流的实现非常简单。 首先,为速率限制器创建一个密钥。 然后,使用密钥查找或创建限制器,最后获取限制器以获取许可。

速率限制器密钥创建中有一个小陷阱。 注释定义的键将转换为可选键,但由于性能原因,不能使用可选键的orElse方法。 可选的orElse方法采用一个在任何情况下(无论是否存在)可选的值。 另一方面,另一种方法或orElseGet使用了供应商,该供应商仅当不存在可选值时才允许懒惰地评估值。 密钥工厂的createKey可能是一项昂贵的操作,因此使用供应商版本。

并发哈希图包含一个方便的方法computeIfAbsent ,该方法自动基于键和已定义的函数查找或创建一个值。 这允许对映射值进行简单明了的延迟初始化。 速率限制器是按需创建的,并且保证每个唯一的限制器密钥只有一个实例。

默认的密钥工厂实现使用JoinPointToStringHelper中的帮助程序方法将连接点转换为文本表示形式。

public class JoinPointToStringHelper {public static String toString(JoinPoint jp) {StringBuilder sb = new StringBuilder();appendType(sb, getType(jp));Signature signature = jp.getSignature();if (signature instanceof MethodSignature) {MethodSignature ms = (MethodSignature) signature;sb.append("#");sb.append(ms.getMethod().getName());sb.append("(");appendTypes(sb, ms.getMethod().getParameterTypes());sb.append(")");}return sb.toString();}private static Class<?> getType(JoinPoint jp) {return Optional.ofNullable(jp.getSourceLocation()).map(SourceLocation::getWithinType).orElse(jp.getSignature().getDeclaringType());}private static void appendTypes(StringBuilder sb, Class<?>[] types) {for (int size = types.length, i = 0; i < size; i++) {appendType(sb, types[i]);if (i < size - 1) {sb.append(",");}}}private static void appendType(StringBuilder sb, Class<?> type) {if (type.isArray()) {appendType(sb, type.getComponentType());sb.append("[]");} else {sb.append(type.getName());}}
}

最后,只需添加@RateLimit批注,即可将限制应用于任何启用Spring的方法。

@Service
public class MyService {...@RateLimit(5)public String callExternalApi() {return restTemplate.getForEntity(url, String.class).getBody();}}

有人可能想知道这种解决方案能否很好地扩展? 不,实际上不是。 Guava的速率限制器会阻止当前线程,因此,如果对受限制的服务进行异步调用,则大量线程将被阻止,并可能导致空闲线程耗尽。 如果在多个应用程序或JVM实例中复制服务,则会引起另一个问题。 没有限制器速率的全局同步。 对于驻留在单个JVM中的单个应用程序以及节流方法的适当负载,此实现效果很好。

进一步阅读:

  • 使用Spring进行面向方面的编程
  • 番石榴RateLimiter
  • RateLimiter –发现Google Guava
  • 有序Java多通道异步节流器
  • 节流演员信息

翻译自: https://www.javacodegeeks.com/2015/07/throttle-methods-with-spring-aop-and-guava-rate-limiter.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337228.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 模板(Template)总结,长点小知识

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删前言&#xff1a;大家好&#xff0c;今天给大家分享一篇关于 C 模板总结概述.模板&#xff08;Template&#xff09;指 C 程序设计设计语言中采用…

归并排序 java_归并排序(Java实现)

package primary_01;/** 归并排序* 1.算法实现* 2.验证算法的正确性* 3.分析算法的复杂度*/public class Merge_sort {public static void main(String[] args) {int arr [] {2,1,6,5,9,8,2020,199};sort(arr, 0, arr.length-1);for (int i : arr) {System.out.print(i" &…

python kotlin_在Python,Java和Kotlin中标记参数和重载

python kotlin在多种语言之间跳来跳去可以帮助您注意到不同语言的习惯用法和最佳做法之间的某些差异。 比较有趣的差异之一与一个函数执行多项操作有关。 Python 我们先来看一下Python。 Python实际上无法重载&#xff0c;因为用相同的名称定义一个新的函数/方法只会覆盖前一个…

python调用zabbixapi接口_python调用zabbix api获取主机信息

import requestsimport jsonheaders {Content-Type: application/json}class GetZabbix:def __init__(self):#用户信息self.username "****"self.password "****"self.url "http://192.168.0.*/zabbix/api_jsonrpc.php"self.token self.get…

C++17 常用新特性:带初始化的 if 和 switch 语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删早在2016-03-14&#xff0c;Thomas Kppe 在https://wg21.link/p0305r0中就提出了在if和switch使用初始化语句&#xff0c;使用时的代码示例如下&…

python回归预测例子_案例实战 | 逻辑回归实现客户流失预测(附Python代码与源数据)...

关注一下~&#xff0c;更多商业数据分析案例等你来撩前言利用逻辑回归进行客户流失预警建模中涵盖了许多比较细的知识点&#xff0c;思维导图只展示了极小的一部分&#xff0c;相关知识点链接将穿插在文中。(源数据源代码空降文末获取)数据读入数据属性说明数据探索简洁版本&am…

latex中算法命令_比较Java 8中的命令式和功能性算法

latex中算法命令Mario Fusco受欢迎的推文令人印象深刻&#xff0c;显示了类似算法的命令性和功能性方法之间的主要区别实际上是&#xff1a; 势在必行–功能分离pic.twitter.com/G2cC6iBkDJ — Mario Fusco&#xff08;mariofusco&#xff09; 2015年3月1日 两种算法都做同样…

四大语言加强统治地位,Rust威胁C/C++

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删TIOBE 公布了 2022 年 10 月的编程语言排行榜。截至今日&#xff0c;Python、Java、C 和 C 语言已经在 TIOBE 榜单的前 4 位盘踞了相当长的一段时…

sql计算留存_SQL无所不能:DBA宝妈宝爸系列分享

原文链接&#xff1a;https://www.modb.pro/db/22315 目前程序从功能上其实已经完全满足客户(当然我这里的客户都是指媳妇儿)需求&#xff0c;具体可参考&#xff1a;使用SQL计算宝宝每次吃奶的时间间隔 - Part1&#xff1a;分析函数的使用使用SQL计算宝宝每次吃奶的时间间隔 -…

java8流分组 性能_Java性能教程– Java 8流有多快?

java8流分组 性能在此JAX Magazine的预览预览中&#xff0c;JAX伦敦发言人Angelika Langer为使用Java流的任何人回答了最重要的问题&#xff1a;它们真的更快吗&#xff1f; Java 8是JDK收集框架的主要补充&#xff0c;即流​​API。 与集合相似&#xff0c;流表示元素序列。 …

对 int 变量赋值的操作是原子的吗?为什么?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删这个是在面试的时候遇到的问题&#xff0c;当时没有答出来。回到家以后查了查&#xff0c;整理记录下来。原问题&#xff1a;什么指令集支持原子…

C语言知识总结:if-else判断语句和switch-case选择语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删1、if-else判断语句一个基本的if语句由一个关键字if开头&#xff0c;跟上在括号&#xff08;&#xff09;里边的是表示逻辑条件的表达式&#xf…

hibernate性能_改善Hibernate应用程序性能的7种方法

hibernate性能Hibernate是将Java Web应用程序与SQL数据库集成的好工具&#xff0c;但是当应用程序的响应时间开始显着增加时&#xff0c;我们应该怎么做&#xff1f; 当我们怀疑应用程序是否会随着客户群的增长而扩展时&#xff0c;我们该怎么办&#xff1f; 在花大价钱托管或…

python 坐标轴 单位刻度_Python学习笔记(matplotlib篇)--坐标轴刻度

python学习笔记--坐标轴刻度所用的库及环境:ide:pycharmpython环境&#xff1a;python3.7matplotlib: matplotlib 1.11numpy&#xff1a; numpy1.15.datetime &#xff1a;datetime坐标轴刻度概念当需要把x&#xff0c;y坐标轴刻度调整的更密集些或者更宽松点学习如何调整x&…

为什么永远不会有语言取代C/C++?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删数以百计的 C/C 替代品将会出现&#xff0c;但 C/C 将永远与我们同在&#xff01;每个 CPU 都带有一种称为 ISA&#xff08;指令集架构&#xff…

mwc校准油门_编写下载服务器。 第五部分:油门下载速度

mwc校准油门在僵尸网络时代&#xff0c;您可以租用几百美元来运行自己的分布式拒绝服务攻击&#xff0c;拥有紧急开关来有选择地关闭昂贵的功能或严重降低性能是一个巨大的胜利。 在缓解问题的同时&#xff0c;您的应用程序仍可运行。 当然&#xff0c;这种安全措施在高峰或工作…

sql 非主键每年自增_或许你不知道的 10 条 SQL 技巧

上文我们简述了 SQL 的一些进阶技巧&#xff0c;一些朋友觉得不过瘾&#xff0c;我们继续来下篇&#xff0c;再送你 10 个技巧一、 使用延迟查询优化 limit [offset], [rows]经常出现类似以下的 SQL 语句:SELECT * FROM film LIMIT 100000, 10offset 特别大!这是我司出现很多慢…

C语言最全入门笔记

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删c语言入门C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好&am…

yolo v4 python_YOLOv4: Darknet 如何于 Ubuntu 编译,及使用 Python 接口

本文将介绍 YOLOv4 官方 Darknet 实现&#xff0c;如何于 Ubuntu 18.04 编译&#xff0c;及使用 Python 接口。主要内容有&#xff1a;准备基础环境&#xff1a; Nvidia Driver, CUDA, cuDNN, CMake, Python编译应用环境&#xff1a; OpenCV, Darknet用预训练模型进行推断&…

junit测试spring_使用Spring JUnit规则进行参数化集成测试

junit测试springSpring 4.2附带了全新的JUnit规则&#xff1a; SpringClassRule和SpringMethodRule 。 使用JUnit规则的主要优点是让开发人员摆脱SpringJUnit4ClassRunner并在Spring集成测试中利用不同的JUnit运行器。 我认为Spring JUnit Rules的最大机会是易于创建参数化的集…