外部服务或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 Framework,在任何情况下都可以使用方面。

@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

使用Spring AOP和Guava速率限制器的节气门方法相关推荐

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

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

  2. 哪些方法不能够实施Spring AOP事务

    2019独角兽企业重金招聘Python工程师标准>>> 哪些方法不能够实施Spring AOP事务 由于Spring事务管理是基于接口代理或动态字节码技术.通过AOP实施事务增强. ...

  3. Spring AOP 切点(pointcut)表达式

    概括 这遍文章将介绍Spring AOP切点表达式(下称表达式)语言,首先介绍两个面向切面编程中使用到的术语. 连接点(Joint Point):广义上来讲,方法.异常处理块.字段这些程序调用过程中可 ...

  4. 顺序执行_执行流程 | 你真的了解Spring AOP的执行顺序吗?

    Hi! 我是小小,我们又见面了,今天的主要内容是,你真的了解Spring AOP的执行顺序吗?跟随着我的脚步,一块丈量世界,了解世界,重新认识,重新了解Spring AOP的执行顺序. 聊一聊毕业四个 ...

  5. gtw-050090|执行拦截器时发生异常_执行流程 | 你真的了解Spring AOP的执行顺序吗?...

    Hi! 我是小小,我们又见面了,今天的主要内容是,你真的了解Spring AOP的执行顺序吗?跟随着我的脚步,一块丈量世界,了解世界,重新认识,重新了解Spring AOP的执行顺序. 聊一聊毕业四个 ...

  6. Spring AOP知识点简介

    文章目录 1.什么是AOP 1.1.AOP术语 1.2.AOP框架 2.动态代理 2.1.JDK动态代理 2.2.CGLIB动态代理 3.基于代理类的AOP实现 3.1.Spring的通知类型 3.2 ...

  7. Spring AOP 功能使用详解

    前言 AOP 既熟悉又陌生,了解过 Spring 人的都知道 AOP 的概念,即面向切面编程,可以用来管理一些和主业务无关的周边业务,如日志记录,事务管理等:陌生是因为在工作中基本没有使用过,AOP ...

  8. 执行流程 | 你真的了解Spring AOP的执行顺序吗?

    Hi! 我是小小,我们又见面了,今天的主要内容是,你真的了解Spring AOP的执行顺序吗?跟随着我的脚步,一块丈量世界,了解世界,重新认识,重新了解Spring AOP的执行顺序. 聊一聊毕业四个 ...

  9. spring aop 会根据实际情况(有无接口)自动选择 两种 动态代理(jdk和cglib)之一...

    资料: (1)Cglib的简单使用: https://blog.csdn.net/zhanghongjie0302/article/details/45648947 (2)关于java字节码框架ASM ...

最新文章

  1. Exchange动态同步中的INTERNET_29错误代码
  2. Memcached深度分析【zz】
  3. Orleans 知多少 | 3. Hello Orleans
  4. ios 高德地图加载瓦片地图_IOS 高德地图 API 加载 WMS 服务
  5. Status Code:200 OK (from disk cache)和304的区别,以及怎么禁止缓存
  6. jmeter压力测试linux,JMeter压力测试
  7. java elasticsearch_在Spring java框架中使用ElasticSearch的最佳方式
  8. android: PendingIntent的使用
  9. Android PreferenceActivity添加ToolBar
  10. CnOpenData国际货物贸易数据
  11. matlab画基尼系数,matlab 拟合洛伦兹曲线求基尼系数
  12. 夏季养生要以“清”为贵
  13. 移动和包不能激活NFC问题
  14. 只有你能听见(Calling you)2
  15. centos恢复图形界面_centos7恢复图形界面_centos7没有图形界面
  16. SSM源码分析之23种设计模式(策略模式和模板模式)
  17. [转]信息安全相关理论题(二)
  18. D - National Railway (DP)
  19. xmms mp3 wma插件
  20. 计算机组成原理复杂机实验总结,计算机组成原理复杂模型机设计_课程设计报告.doc...

热门文章

  1. composer配置阿里云镜像
  2. Java中的TreeSet集合会自动将元素升序排序
  3. ps中将图片拖不进ps的编辑区的解决方法
  4. 程序员常用网站收藏[定期更新]——csdn博客
  5. 新闻发布项目——访问温馨提示
  6. mysql-on duplicate key update实现insertOrUpdate官方文档
  7. 单件模式(单例模式)
  8. 不相交集ADT(联机算法 + 脱机算法)
  9. quarkus_Quarkus入门
  10. java 方法 示例_Java 9示例–收集的工厂方法–创建不可修改的列表,集合和映射...