Java限流神器:手写一个通用限流任务执行器,支持重试和指数退避!

张开发
2026/4/7 3:14:56 15 分钟阅读

分享文章

Java限流神器:手写一个通用限流任务执行器,支持重试和指数退避!
调用第三方API最怕什么怕被限流今天分享一个自制的限流任务执行器能帮你轻松控制请求频率还能自动重试失败任务指数退避不添乱。代码可直接复制到项目中使用 背景痛点很多场景下我们需要控制任务的执行速率调用淘宝IP接口对方限制每秒最多1次请求批量请求第三方API担心触发限流封禁任务可能因网络抖动失败需要自动重试今天要介绍的RateLimitedExecutor就是为解决这些问题而生的。 核心功能✅限流执行– 每秒最多执行N个任务超出则排队等待✅顺序保证– 任务严格按照提交顺序执行✅自动重试– 失败后自动重试可配置最大次数✅指数退避– 支持退避延迟策略避免加重服务端压力✅异步返回– 使用CompletableFuture获取结果不阻塞主线程 类设计完整代码importcom.google.common.util.concurrent.RateLimiter;importjava.util.concurrent.*;importjava.util.function.Function;/** * 限流任务执行器按固定速率顺序执行任务支持重试。 * param T 任务返回值类型 */publicclassRateLimitedExecutorT{privatefinalBlockingQueueTaskWrapperTqueuenewLinkedBlockingQueue();privatefinalRateLimiterrateLimiter;privatefinalintmaxRetries;privatefinallonginitialDelayMs;// 首次重试延迟毫秒privatefinaldoublebackoffMultiplier;// 退避乘数如2.0表示每次翻倍privatefinalExecutorServiceworkerExecutors.newSingleThreadExecutor(r-{ThreadtnewThread(r,RateLimitedExecutor-Worker);t.setDaemon(true);// 设为守护线程避免阻止JVM退出returnt;});privatevolatilebooleanrunningtrue;/** * 构造限流执行器 * param permitsPerSecond 每秒允许执行的任务数如1.0表示每秒1次 * param maxRetries 最大重试次数不含首次执行 * param initialDelayMs 首次重试延迟毫秒数 * param backoffMultiplier 退避乘数1.0表示固定延迟1.0表示指数退避 */publicRateLimitedExecutor(doublepermitsPerSecond,intmaxRetries,longinitialDelayMs,doublebackoffMultiplier){this.rateLimiterRateLimiter.create(permitsPerSecond);this.maxRetriesmaxRetries;this.initialDelayMsinitialDelayMs;this.backoffMultiplierbackoffMultiplier;worker.submit(this::process);}/** * 提交一个任务返回CompletableFuture异步获取结果 * param task 需要执行的任务Callable * return 代表异步结果的CompletableFuture */publicCompletableFutureTsubmit(CallableTtask){CompletableFutureTfuturenewCompletableFuture();queue.offer(newTaskWrapper(task,future));returnfuture;}// 工作线程主循环privatevoidprocess(){while(running){try{TaskWrapperTwrapperqueue.take();// 阻塞直到有任务executeWithRetry(wrapper);}catch(InterruptedExceptione){Thread.currentThread().interrupt();break;}}}// 执行单个任务带重试privatevoidexecuteWithRetry(TaskWrapperTwrapper){intretries0;longdelayinitialDelayMs;while(retriesmaxRetries){// 限流获取令牌若不足则阻塞rateLimiter.acquire();try{Tresultwrapper.task.call();wrapper.future.complete(result);return;// 成功结束}catch(Exceptione){retries;if(retriesmaxRetries){wrapper.future.completeExceptionally(e);return;}// 重试等待退避try{Thread.sleep(delay);}catch(InterruptedExceptionie){Thread.currentThread().interrupt();wrapper.future.completeExceptionally(ie);return;}// 更新下次重试延迟delay(long)(delay*backoffMultiplier);}}}/** * 优雅关闭执行器等待已提交任务执行完毕不再接受新任务 */publicvoidshutdown(){runningfalse;worker.shutdown();// 不再接受新任务try{if(!worker.awaitTermination(5,TimeUnit.SECONDS)){worker.shutdownNow();}}catch(InterruptedExceptione){worker.shutdownNow();Thread.currentThread().interrupt();}}/** * 立即关闭执行器尝试中断正在执行的任务 */publicvoidshutdownNow(){runningfalse;worker.shutdownNow();}// 内部任务包装类privatestaticclassTaskWrapperT{finalCallableTtask;finalCompletableFutureTfuture;TaskWrapper(CallableTtask,CompletableFutureTfuture){this.tasktask;this.futurefuture;}}} 依赖要求项目需要引入Guava提供RateLimiterdependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion32.1.2-jre/version/dependency⚙️ 构造参数说明参数类型说明permitsPerSeconddouble每秒允许执行的任务数。例1.0→每秒1次0.5→每2秒1次maxRetriesint最大重试次数不含首次执行。0表示不重试initialDelayMslong首次重试前的等待时间毫秒backoffMultiplierdouble退避乘数。2.0→每次延迟翻倍1.0→固定延迟️ 核心方法CompletableFutureT submit(CallableT task)提交任务返回CompletableFuture可异步获取结果或异常。void shutdown()优雅关闭等待已提交任务执行完毕不再接受新任务。void shutdownNow()立即关闭尝试中断当前执行的任务。 注意事项执行器内部使用单线程处理任务严格保证提交顺序。限流基于RateLimiter每次执行前阻塞直到获取令牌因此即使任务执行时间极短也能保证速率限制。重试期间工作线程会阻塞等待后续任务不会提前执行顺序性得以保持。工作线程默认设为守护线程当所有用户线程结束时 JVM 会自动退出无需手动关闭。但建议在应用关闭时调用shutdown()以确保任务完整执行。 实战示例调用淘宝IP接口假设淘宝IP接口地址为http://ip.taobao.com/outGetIpInfo?ip{ip}我们需要限制每秒 1 次请求失败重试 3 次首次重试延迟 1 秒指数退避乘数 2.0Demo 代码publicclassTaobaoIpDemo{publicstaticvoidmain(String[]args)throwsException{// 创建限流执行器每秒1次重试3次首次延迟1秒指数退避2.0RateLimitedExecutorStringexecutornewRateLimitedExecutor(1.0,// 每秒1次3,// 重试3次1000,// 首次延迟1秒2.0// 指数退避);// 需要查询的IP列表String[]ips{8.8.8.8,114.114.114.114,223.5.5.5};// 提交所有任务for(Stringip:ips){CompletableFutureStringfutureexecutor.submit(()-queryIp(ip));// 异步处理结果future.thenAccept(result-{System.out.println(IP: ip, 结果: result);}).exceptionally(ex-{System.err.println(IP: ip, 查询失败: ex.getMessage());returnnull;});}// 等待所有任务完成实际应用中不需要这里仅演示Thread.sleep(10000);// 优雅关闭executor.shutdown();}privatestaticStringqueryIp(Stringip){Stringurlhttps://ip.taobao.com/outGetIpInfo?accessKeyalibaba-incipip;returnRestClient.create().get().uri(url).retrieve().body(String.class);}} 适用场景调用第三方API需要严格限制QPS如淘宝IP、微信接口、百度地图等需要按顺序执行任务如写入文件、顺序处理消息任务可能临时失败需要自动重试网络抖动、服务端限流希望重试策略为指数退避避免雪崩效应 总结这个轻量级的限流任务执行器代码简洁、功能完整能帮你轻松解决速率控制 顺序执行 自动重试三大问题。配合CompletableFuture异步编程性能与体验兼得。如果你也在为API限流或任务重试头疼不妨复制这份代码到项目中试试觉得有用欢迎点赞、在看、转发给更多小伙伴也欢迎在评论区交流你的使用心得或改进建议 本文代码已脱敏可放心复制到生产项目。Guava 版本建议使用 30.0 以上。 源码获取方式搜索公众号「秋云编程」并关注回复 demo 即可。**

更多文章