javascript
使用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速率限制器的节气门方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux中jdk配置(linux的jd
- 下一篇: 安卓永恒战士2最新破解版(安卓永恒战士)