最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

基于Java手寫一個通用限流任務(wù)執(zhí)行器

 更新時間:2026年04月07日 09:12:30   作者:秋云編程  
調(diào)用第三方API最怕什么,那就是怕被限流,今天分享一個自制的限流任務(wù)執(zhí)行器,能幫你輕松控制請求頻率,還能自動重試失敗任務(wù),指數(shù)退避不添亂,快跟隨小編一起學(xué)習(xí)起來吧

調(diào)用第三方API最怕什么?怕被限流!今天分享一個自制的限流任務(wù)執(zhí)行器,能幫你輕松控制請求頻率,還能自動重試失敗任務(wù),指數(shù)退避不添亂。代碼可直接復(fù)制到項目中使用~

背景痛點

很多場景下我們需要控制任務(wù)的執(zhí)行速率:

  • 調(diào)用淘寶IP接口,對方限制每秒最多1次請求
  • 批量請求第三方API,擔(dān)心觸發(fā)限流封禁
  • 任務(wù)可能因網(wǎng)絡(luò)抖動失敗,需要自動重試

今天要介紹的 RateLimitedExecutor 就是為解決這些問題而生的。

核心功能

限流執(zhí)行 – 每秒最多執(zhí)行N個任務(wù),超出則排隊等待

順序保證 – 任務(wù)嚴格按照提交順序執(zhí)行

自動重試 – 失敗后自動重試(可配置最大次數(shù))

指數(shù)退避 – 支持退避延遲策略,避免加重服務(wù)端壓力

異步返回 – 使用 CompletableFuture 獲取結(jié)果,不阻塞主線程

類設(shè)計(完整代碼)

import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.*;
import java.util.function.Function;

/**
 * 限流任務(wù)執(zhí)行器:按固定速率順序執(zhí)行任務(wù),支持重試。
 * @param <T> 任務(wù)返回值類型
 */
public class RateLimitedExecutor<T> {
    private final BlockingQueue<TaskWrapper<T>> queue = new LinkedBlockingQueue<>();
    private final RateLimiter rateLimiter;
    private final int maxRetries;
    private final long initialDelayMs;    // 首次重試延遲(毫秒)
    private final double backoffMultiplier; // 退避乘數(shù)(如2.0表示每次翻倍)
    private final ExecutorService worker = Executors.newSingleThreadExecutor(r -> {
        Thread t = new Thread(r, "RateLimitedExecutor-Worker");
        t.setDaemon(true);  // 設(shè)為守護線程,避免阻止JVM退出
        return t;
    });
    private volatile boolean running = true;

    /**
     * 構(gòu)造限流執(zhí)行器
     * @param permitsPerSecond 每秒允許執(zhí)行的任務(wù)數(shù)(如1.0表示每秒1次)
     * @param maxRetries       最大重試次數(shù)(不含首次執(zhí)行)
     * @param initialDelayMs   首次重試延遲毫秒數(shù)
     * @param backoffMultiplier 退避乘數(shù)(1.0表示固定延遲,>1.0表示指數(shù)退避)
     */
    public RateLimitedExecutor(double permitsPerSecond, int maxRetries,
                               long initialDelayMs, double backoffMultiplier) {
        this.rateLimiter = RateLimiter.create(permitsPerSecond);
        this.maxRetries = maxRetries;
        this.initialDelayMs = initialDelayMs;
        this.backoffMultiplier = backoffMultiplier;
        worker.submit(this::process);
    }

    /**
     * 提交一個任務(wù),返回CompletableFuture異步獲取結(jié)果
     * @param task 需要執(zhí)行的任務(wù)(Callable)
     * @return 代表異步結(jié)果的CompletableFuture
     */
    public CompletableFuture<T> submit(Callable<T> task) {
        CompletableFuture<T> future = new CompletableFuture<>();
        queue.offer(new TaskWrapper<>(task, future));
        return future;
    }

    // 工作線程主循環(huán)
    private void process() {
        while (running) {
            try {
                TaskWrapper<T> wrapper = queue.take(); // 阻塞直到有任務(wù)
                executeWithRetry(wrapper);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }

    // 執(zhí)行單個任務(wù)(帶重試)
    private void executeWithRetry(TaskWrapper<T> wrapper) {
        int retries = 0;
        long delay = initialDelayMs;
        while (retries <= maxRetries) {
            // 限流:獲取令牌,若不足則阻塞
            rateLimiter.acquire();

            try {
                T result = wrapper.task.call();
                wrapper.future.complete(result);
                return; // 成功,結(jié)束
            } catch (Exception e) {
                retries++;
                if (retries > maxRetries) {
                    wrapper.future.completeExceptionally(e);
                    return;
                }
                // 重試等待(退避)
                try {
                    Thread.sleep(delay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    wrapper.future.completeExceptionally(ie);
                    return;
                }
                // 更新下次重試延遲
                delay = (long) (delay * backoffMultiplier);
            }
        }
    }

    /**
     * 優(yōu)雅關(guān)閉執(zhí)行器:等待已提交任務(wù)執(zhí)行完畢,不再接受新任務(wù)
     */
    public void shutdown() {
        running = false;
        worker.shutdown(); // 不再接受新任務(wù)
        try {
            if (!worker.awaitTermination(5, TimeUnit.SECONDS)) {
                worker.shutdownNow();
            }
        } catch (InterruptedException e) {
            worker.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 立即關(guān)閉執(zhí)行器,嘗試中斷正在執(zhí)行的任務(wù)
     */
    public void shutdownNow() {
        running = false;
        worker.shutdownNow();
    }

    // 內(nèi)部任務(wù)包裝類
    private static class TaskWrapper<T> {
        final Callable<T> task;
        final CompletableFuture<T> future;

        TaskWrapper(Callable<T> task, CompletableFuture<T> future) {
            this.task = task;
            this.future = future;
        }
    }
}

依賴要求

項目需要引入 Guava(提供 RateLimiter):

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.2-jre</version>
</dependency>

構(gòu)造參數(shù)說明

參數(shù)類型說明
permitsPerSeconddouble每秒允許執(zhí)行的任務(wù)數(shù)。例:1.0→每秒1次,0.5→每2秒1次
maxRetriesint最大重試次數(shù)(不含首次執(zhí)行)。0表示不重試
initialDelayMslong首次重試前的等待時間(毫秒)
backoffMultiplierdouble退避乘數(shù)。2.0→每次延遲翻倍;1.0→固定延遲

核心方法

CompletableFuture<T> submit(Callable<T> task):提交任務(wù),返回 CompletableFuture,可異步獲取結(jié)果或異常。

void shutdown():優(yōu)雅關(guān)閉:等待已提交任務(wù)執(zhí)行完畢,不再接受新任務(wù)。

void shutdownNow():立即關(guān)閉:嘗試中斷當前執(zhí)行的任務(wù)。

注意事項

執(zhí)行器內(nèi)部使用 單線程 處理任務(wù),嚴格保證提交順序

限流基于 RateLimiter,每次執(zhí)行前阻塞直到獲取令牌,因此即使任務(wù)執(zhí)行時間極短,也能保證速率限制。

重試期間工作線程會阻塞等待,后續(xù)任務(wù)不會提前執(zhí)行,順序性得以保持。

工作線程默認設(shè)為守護線程,當所有用戶線程結(jié)束時 JVM 會自動退出,無需手動關(guān)閉。但建議在應(yīng)用關(guān)閉時調(diào)用 shutdown() 以確保任務(wù)完整執(zhí)行。

實戰(zhàn)示例:調(diào)用淘寶IP接口

假設(shè)淘寶IP接口地址為 http://ip.taobao.com/outGetIpInfo?ip={ip},我們需要:

  • 限制每秒 1 次請求
  • 失敗重試 3 次
  • 首次重試延遲 1 秒,指數(shù)退避乘數(shù) 2.0

Demo 代碼

public class TaobaoIpDemo {
    public static void main(String[] args) throws Exception {
        // 創(chuàng)建限流執(zhí)行器:每秒1次,重試3次,首次延遲1秒,指數(shù)退避2.0
        RateLimitedExecutor<String> executor = new RateLimitedExecutor<>(
            1.0,    // 每秒1次
            3,      // 重試3次
            1000,   // 首次延遲1秒
            2.0     // 指數(shù)退避
        );

        // 需要查詢的IP列表
        String[] ips = {"8.8.8.8", "114.114.114.114", "223.5.5.5"};

        // 提交所有任務(wù)
        for (String ip : ips) {
            CompletableFuture<String> future = executor.submit(() -> queryIp(ip));

            // 異步處理結(jié)果
            future.thenAccept(result -> {
                System.out.println("IP: " + ip + ", 結(jié)果: " + result);
            }).exceptionally(ex -> {
                System.err.println("IP: " + ip + ", 查詢失敗: " + ex.getMessage());
                return null;
            });
        }

        // 等待所有任務(wù)完成(實際應(yīng)用中不需要,這里僅演示)
        Thread.sleep(10000);

        // 優(yōu)雅關(guān)閉
        executor.shutdown();
    }

    private static String queryIp(String ip) {
        String url = "https://ip.taobao.com/outGetIpInfo?accessKey=alibaba-inc&ip=" + ip;
        return RestClient.create()
                .get()
                .uri(url)
                .retrieve()
                .body(String.class);
    }
}

適用場景

  • 調(diào)用第三方API需要嚴格限制QPS(如淘寶IP、微信接口、百度地圖等)
  • 需要按順序執(zhí)行任務(wù)(如寫入文件、順序處理消息)
  • 任務(wù)可能臨時失敗,需要自動重試(網(wǎng)絡(luò)抖動、服務(wù)端限流)
  • 希望重試策略為指數(shù)退避,避免雪崩效應(yīng)

總結(jié)

這個輕量級的限流任務(wù)執(zhí)行器,代碼簡潔、功能完整,能幫你輕松解決速率控制 + 順序執(zhí)行 + 自動重試三大問題。配合 CompletableFuture 異步編程,性能與體驗兼得。

如果你也在為API限流或任務(wù)重試頭疼,不妨復(fù)制這份代碼到項目中試試~

本文代碼已脫敏,可放心復(fù)制到生產(chǎn)項目。Guava 版本建議使用 30.0 以上。

以上就是基于Java手寫一個通用限流任務(wù)執(zhí)行器的詳細內(nèi)容,更多關(guān)于Java限流任務(wù)執(zhí)行器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 關(guān)于java中@Async異步調(diào)用詳細解析附代碼

    關(guān)于java中@Async異步調(diào)用詳細解析附代碼

    本文主要介紹了java關(guān)于@Async異步調(diào)用詳細解析附代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Spring Boot項目如何同時支持HTTP和HTTPS協(xié)議的實現(xiàn)

    Spring Boot項目如何同時支持HTTP和HTTPS協(xié)議的實現(xiàn)

    這篇文章主要介紹了Spring Boot項目如何同時支持HTTP和HTTPS協(xié)議的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • Java實現(xiàn)MinIO文件上傳的加解密操作

    Java實現(xiàn)MinIO文件上傳的加解密操作

    在云存儲場景中,數(shù)據(jù)安全是核心需求之一,MinIO作為高性能對象存儲服務(wù),支持通過客戶端加密(CSE)在數(shù)據(jù)上傳前完成加密,下面我們來看看如何通過Java實現(xiàn)MinIO文件的加密上傳與解密下載吧
    2025-05-05
  • Java深入探究關(guān)鍵字abstract的使用

    Java深入探究關(guān)鍵字abstract的使用

    如果一個方法使用 abstract 來修飾,則說明該方法是抽象方法,抽象方法只有聲明沒有實現(xiàn)。需要注意的是 abstract 關(guān)鍵字只能用于普通方法,不能用于 static 方法或者構(gòu)造方法中
    2022-05-05
  • Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景

    Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景

    ReentrantReadWriteLock是Java中一種高效的讀寫鎖,適用于讀多寫少的并發(fā)場景,它通過允許多個線程同時讀取,但在寫入時限制為單線程訪問,從而提高了程序的并發(fā)性和性能,本文給大家介紹Java并發(fā)讀寫鎖ReentrantReadWriteLock 使用場景,感興趣的朋友跟隨小編一起看看吧
    2024-10-10
  • Jdbctemplate多數(shù)據(jù)源配置方法詳解

    Jdbctemplate多數(shù)據(jù)源配置方法詳解

    這篇文章主要介紹了Jdbctemplate多數(shù)據(jù)源配置方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-06-06
  • Java實現(xiàn)調(diào)用第三方相關(guān)接口

    Java實現(xiàn)調(diào)用第三方相關(guān)接口

    最近在做一個項目,需要調(diào)用第三方接口,本文主要介紹了Java實現(xiàn)調(diào)用第三方相關(guān)接口,具有一定的參考價值,感興趣的可以了解一下
    2023-09-09
  • SpringBoot的異常處理流程是什么樣的?

    SpringBoot的異常處理流程是什么樣的?

    今天給大家?guī)淼氖荍ava的相關(guān)知識,文章圍繞著SpringBoot的異常處理流程展開,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • SpringBoot+Vue跨域配置(CORS)問題得解決過程

    SpringBoot+Vue跨域配置(CORS)問題得解決過程

    在使用 Spring Boot 和 Vue 開發(fā)前后端分離的項目時,跨域資源共享(CORS)問題是一個常見的挑戰(zhàn),接下來,我將分享我是如何一步步解決這個問題的,包括中間的一些試錯過程,希望能夠幫助到正在經(jīng)歷類似問題的你
    2024-08-08
  • Spring Security基于JWT登錄認證的項目實踐

    Spring Security基于JWT登錄認證的項目實踐

    JWT被用來在身份提供者和服務(wù)提供者間傳遞被認證的用戶身份信息,本文主要介紹了Spring Security基于JWT登錄認證的項目實踐,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評論

灵武市| 东山县| 潜山县| 特克斯县| 临西县| 河南省| 罗源县| 广东省| 吕梁市| 公主岭市| 邯郸市| 得荣县| 临高县| 文化| 天等县| 云霄县| 利津县| 宁强县| 陇南市| 腾冲县| 孙吴县| 清涧县| 湘乡市| 清原| 赤壁市| 三河市| 肇庆市| 越西县| 大埔县| 正蓝旗| 桐城市| 遵义市| 诸暨市| 沂南县| 长沙市| 夏河县| 贵阳市| 民县| 永德县| 枞阳县| 班戈县|