日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

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

發(fā)布時間:2023/12/3 javascript 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用Spring AOP和番石榴速率限制器的节气门方法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

外部服務(wù)或API可能有使用限制,或者它們不能失敗就無法處理大量請求。 這篇文章解釋了如何創(chuàng)建一個基于Spring Framework的方面,該方面可以用來限制使用Guava速率限制器的任何建議方法調(diào)用。 以下實(shí)現(xiàn)需要Java 8,Spring AOP和Guava。

讓我們從注釋開始,該注釋用于建議任何啟用Spring AOP的方法調(diào)用。

@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 "";}

注釋定義了兩件事:每秒查詢(或許可)中的速率限制,以及標(biāo)識速率限制器的可選鍵。 如果密鑰相等,則多種方法可以使用相同的速率限制器。 例如,當(dāng)使用來自不同方法的不同參數(shù)調(diào)用API時,每秒所需的總查詢次數(shù)將不會超過。

接下來是實(shí)際的節(jié)流方面,它是作為Spring Framework組件實(shí)現(xiàn)的。 無論有沒有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));} }

該類定義了密鑰工廠的附加接口和默認(rèn)實(shí)現(xiàn),如果注釋未為速率限制器提供顯式密鑰,則使用該默認(rèn)工廠。 密鑰工廠可以使用連接點(diǎn)(基本上是方法調(diào)用)和提供的注釋為速率限制器創(chuàng)建合適的密鑰。 該方面還使用并發(fā)哈希圖來存儲速率限制器實(shí)例。 方面被定義為單例,但是可以從多個線程調(diào)用rateLimit方法,因此并發(fā)哈希圖確保我們?yōu)槊總€唯一鍵僅分配單個速率限制器。 方面的構(gòu)造器注入利用了Spring Framework的可選注入支持。 如果在上下文中未定義KeyFactory bean,則使用默認(rèn)的密鑰工廠。

該類使用@Aspect和@Component進(jìn)行注釋,以便Spring理解已定義的方面并啟用@Before建議。 @Before通知僅包含一個切入點(diǎn),該切入點(diǎn)需要RateLimit批注并將其綁定到方法的limit參數(shù)。 節(jié)流的實(shí)現(xiàn)非常簡單。 首先,為速率限制器創(chuàng)建一個密鑰。 然后,使用密鑰查找或創(chuàng)建限制器,最后獲取限制器以獲取許可。

速率限制器密鑰創(chuàng)建中有一個小陷阱。 注釋定義的鍵將轉(zhuǎn)換為可選鍵,但由于性能原因,不能使用可選鍵的orElse方法。 可選的orElse方法采用一個在任何情況下(無論是否存在)可選的值。 另一方面,另一種方法或orElseGet使用了供應(yīng)商,該供應(yīng)商僅當(dāng)不存在可選值時才允許懶惰地評估值。 密鑰工廠的createKey可能是一項昂貴的操作,因此使用供應(yīng)商版本。

并發(fā)哈希圖包含一個方便的方法computeIfAbsent ,該方法自動基于鍵和已定義的函數(shù)查找或創(chuàng)建一個值。 這允許對映射值進(jìn)行簡單明了的延遲初始化。 速率限制器是按需創(chuàng)建的,并且保證每個唯一的限制器密鑰只有一個實(shí)例。

默認(rèn)的密鑰工廠實(shí)現(xiàn)使用JoinPointToStringHelper中的幫助程序方法將連接點(diǎn)轉(zhuǎn)換為文本表示形式。

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批注,即可將限制應(yīng)用于任何啟用Spring的方法。

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

有人可能想知道這種解決方案能否很好地擴(kuò)展? 不,實(shí)際上不是。 Guava的速率限制器會阻止當(dāng)前線程,因此,如果對受限制的服務(wù)進(jìn)行異步調(diào)用,則大量線程將被阻止,并可能導(dǎo)致空閑線程耗盡。 如果在多個應(yīng)用程序或JVM實(shí)例中復(fù)制服務(wù),則會引起另一個問題。 沒有限制器速率的全局同步。 對于駐留在單個JVM中的單個應(yīng)用程序以及節(jié)流方法的適當(dāng)負(fù)載,此實(shí)現(xiàn)效果很好。

進(jìn)一步閱讀:

  • 使用Spring進(jìn)行面向方面的編程
  • 番石榴RateLimiter
  • RateLimiter –發(fā)現(xiàn)Google Guava
  • 有序Java多通道異步節(jié)流器
  • 節(jié)流演員信息

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

總結(jié)

以上是生活随笔為你收集整理的使用Spring AOP和番石榴速率限制器的节气门方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。