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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

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

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

外部服務或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速率限制器的节气门方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。