百度网盘不限速被限速_基本API限速
百度網(wǎng)盤不限速被限速
您可能正在開發(fā)某種形式的(Web / RESTful)API,并且如果它是面向公眾的(甚至是內(nèi)部的),則通常需要以某種方式對其進(jìn)行速率限制。 即,限制一段時間內(nèi)執(zhí)行的請求數(shù),以節(jié)省資源并防止濫用。
這可能可以通過一些聰明的配置在Web服務(wù)器/負(fù)載均衡器級別上實現(xiàn),但是通常您希望速率限制器是特定于客戶端的(即,API的每個客戶端都應(yīng)具有單獨的速率限制),以及客戶端的方式被確定是不同的。 可能仍然可以在負(fù)載均衡器上執(zhí)行此操作,但是我認(rèn)為將其放在應(yīng)用程序級別上是有意義的。
我將使用spring-mvc作為示例,但是任何Web框架都有插入攔截器的好方法。
所以這是一個spring-mvc攔截器的例子:
@Component public class RateLimitingInterceptor extends HandlerInterceptorAdapter {private static final Logger logger = LoggerFactory.getLogger(RateLimitingInterceptor.class);@Value("${rate.limit.enabled}")private boolean enabled;@Value("${rate.limit.hourly.limit}")private int hourlyLimit;private Map<String, Optional<SimpleRateLimiter>> limiters = new ConcurrentHashMap<>();@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {if (!enabled) {return true;}String clientId = request.getHeader("Client-Id");// let non-API requests passif (clientId == null) {return true;}SimpleRateLimiter rateLimiter = getRateLimiter(clientId);boolean allowRequest = limiter.tryAcquire();if (!allowRequest) {response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());}response.addHeader("X-RateLimit-Limit", String.valueOf(hourlyLimit));return allowRequest;}private SimpleRateLimiter getRateLimiter(String clientId) {if (limiters.containsKey(clientId)) {return limiters.get(clientId);} else {synchronized(clientId.intern()) {// double-checked locking to avoid multiple-reinitializationsif (limiters.containsKey(clientId)) {return limiters.get(clientId);}SimpleRateLimiter rateLimiter = createRateLimiter(clientId);limiters.put(clientId, rateLimiter);return rateLimiter;}}}@PreDestroypublic void destroy() {// loop and finalize all limiters} }這將按需初始化每個客戶端的速率限制器。 另外,在啟動時,您可以遍歷所有已注冊的API客戶端,并為每個客戶端創(chuàng)建一個速率限制器。 如果速率限制器不允許更多請求(tryAcquire()返回false),則取消“ Too many requests”(太多請求)并中止請求的執(zhí)行(從攔截器返回“ false”)。
這聽起來很簡單。 但是有一些問題。 您可能想知道上面的SimpleRateLimiter在哪里定義。 我們將到達(dá)那里,但首先讓我們看看我們對速率限制器實現(xiàn)有哪些選擇。
最受歡迎的似乎是番石榴RateLimiter 。 它具有簡單的工廠方法,可為您提供指定速率(每秒允許)的速率限制器。 但是,它不能很好地適應(yīng)Web API,因為您無法使用預(yù)先存在的許可數(shù)量初始化RateLimiter。 這意味著在限制器允許請求之前,應(yīng)經(jīng)過一段時間。 還有另一個問題–如果您每秒的許可數(shù)量少于一個(例如,如果您希望的速率限制為“每小時200個請求”),則可以傳遞一個分?jǐn)?shù)(hourlyLimit / secondsInHour),但仍然無法達(dá)到您的目的可以預(yù)期,因為內(nèi)部有一個“ maxPermits”字段,可以將許可證數(shù)量的上限限制為比您想要的要少得多。 此外,速率限制器不允許突發(fā)-您每秒恰好有X個許可,但您不能長時間分散它們,例如在一秒鐘內(nèi)有5個請求,然后在接下來的幾秒鐘內(nèi)沒有請求。 實際上,上述所有問題都可以解決,但遺憾的是,可以通過您無法訪問的隱藏字段來解決。 多年來,存在多個功能請求,但是Guava不會更新速率限制器,從而使其不適用于API速率限制。
使用反射,您可以調(diào)整參數(shù)并使限制器工作。 但是,這很丑陋,并且不能保證它會按預(yù)期工作。 我在這里展示了如何使用每小時X許可,爆破性和完整的初始許可來初始化番石榴速率限制器。 當(dāng)我認(rèn)為這樣做的時候,我看到tryAcquire()有一塊tryAcquire() synchronized(..)塊。 這是否意味著在僅檢查是否允許發(fā)出請求時,所有請求都會彼此等待? 那太可怕了。
因此,實際上番石榴RateLimiter并非旨在(網(wǎng)絡(luò))API速率限制。 番石榴(Guava)勸阻人們不要濫用它的方法,也許保持其功能欠佳?
這就是為什么我決定根據(jù)Java信號量自己實現(xiàn)一些簡單的事情。 這是樸素的實現(xiàn) :
public class SimpleRateLimiter {private Semaphore semaphore;private int maxPermits;private TimeUnit timePeriod;private ScheduledExecutorService scheduler;public static SimpleRateLimiter create(int permits, TimeUnit timePeriod) {SimpleRateLimiter limiter = new SimpleRateLimiter(permits, timePeriod);limiter.schedulePermitReplenishment();return limiter;}private SimpleRateLimiter(int permits, TimeUnit timePeriod) {this.semaphore = new Semaphore(permits);this.maxPermits = permits;this.timePeriod = timePeriod;}public boolean tryAcquire() {return semaphore.tryAcquire();}public void stop() {scheduler.shutdownNow();}public void schedulePermitReplenishment() {scheduler = Executors.newScheduledThreadPool(1);scheduler.schedule(() -> {semaphore.release(maxPermits - semaphore.availablePermits());}, 1, timePeriod);} }它需要一定數(shù)量的許可(允許的請求數(shù)量)和一段時間。 時間段為“ 1 X”,其中X可以是秒/分鐘/小時/每天-取決于您希望如何配置限制-每秒,每分鐘,每小時,每天。 調(diào)度程序每1 X補充所獲得的許可證。 無法控制突發(fā)事件(客戶端可以用快速連續(xù)的請求花費所有許可),沒有熱身功能,沒有逐步的補充。 根據(jù)您的需要,這可能并不理想,但這只是一個基本的速率限制器,它是線程安全的,沒有任何阻塞。 我編寫了一個單元測試,以確認(rèn)限制器的行為正確,并且還對本地應(yīng)用程序進(jìn)行了性能測試,以確保遵守限制。 到目前為止,它似乎正在工作。
有其他選擇嗎? 好吧,是的–像RateLimitJ這樣的庫使用Redis來實現(xiàn)速率限制。 但是,這意味著您需要設(shè)置和運行Redis。 對于“簡單地”進(jìn)行速率限制,這似乎是開銷。
另一方面,限速將如何在一組應(yīng)用程序節(jié)點中正常工作? 應(yīng)用程序節(jié)點可能需要一些數(shù)據(jù)庫或八卦協(xié)議來共享有關(guān)剩余的每個客戶端許可(請求)的數(shù)據(jù)? 不必要。 解決此問題的一種非常簡單的方法是假設(shè)負(fù)載均衡器在節(jié)點之間平均分配負(fù)載。 這樣,您只需要將每個節(jié)點上的限制設(shè)置為等于總限制除以節(jié)點數(shù)即可。 它不是很精確,但是您很少需要做到這一點–允許5-10個以上的請求不會終止您的應(yīng)用程序,允許5-10個以下的請求對用戶來說并不算太大。
但是,那將意味著您必須知道應(yīng)用程序節(jié)點的數(shù)量。 如果您使用自動縮放(例如在AWS中),則節(jié)點數(shù)可能會根據(jù)負(fù)載而變化。 在這種情況下,補給排定的作業(yè)無需配置硬編碼的許可證數(shù)量,而是可以通過調(diào)用AWS(或其他云提供商)API來獲取其中的節(jié)點數(shù)量,從而動態(tài)計算“ maxPermits”。當(dāng)前的自動縮放組。 這比僅支持Redis部署要簡單得多。
總的來說,我很驚訝沒有一種“規(guī)范”的方法來實現(xiàn)速率限制(在Java中)。 限制速率的需求可能并不像看起來那樣普遍。 或者,它是手動實施的–通過臨時禁止使用“資源過多”的API客戶端。
翻譯自: https://www.javacodegeeks.com/2017/07/basic-api-rate-limiting.html
百度網(wǎng)盤不限速被限速
總結(jié)
以上是生活随笔為你收集整理的百度网盘不限速被限速_基本API限速的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 温州是几线城市(二线城市之浙江温州市经济
- 下一篇: 小米5安卓pixel刷机包(小米5安卓p
