diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/RateLimiter.java b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/RateLimiter.java new file mode 100644 index 000000000..6d701e0cb --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/RateLimiter.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Hutool Team. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.core.thread.ratelimiter; + +/** + * 限流接口
+ * 通过实现此接口以实现不同的限流策略,如令牌桶、分布式限流等 + * + * @author Looly + * @since 6.0.0 + */ +public interface RateLimiter { + + /** + * 尝试获取许可,非阻塞方法 + * + * @param permits 需要的许可数 + * @return {@code true}表示成功获取,{@code false}表示无足够的许可可获取,此时需要等待给定的时间 + */ + boolean tryAcquire(int permits); +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/RateLimiterConfig.java b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/RateLimiterConfig.java new file mode 100644 index 000000000..65d625756 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/RateLimiterConfig.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024 Hutool Team. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.core.thread.ratelimiter; + +/** + * 限流通用配置 + * + * @author Looly + * @since 6.0.0 + */ +public class RateLimiterConfig { + + /** + * 创建限流配置 + * + * @param timeoutDuration 超时时间,即超过这个时间没有获取到许可,则返回false + * @param limitRefreshPeriod 限制刷新周期,即每多少时间刷新一次 + * @param limitForPeriod 个周期的许可数 + * @return RateLimiterConfig + */ + public static RateLimiterConfig of(final long timeoutDuration, final long limitRefreshPeriod, final int limitForPeriod) { + return new RateLimiterConfig(timeoutDuration, limitRefreshPeriod, limitForPeriod); + } + + /** + * 超时时间,即超过这个时间没有获取到许可,则返回false,单位毫秒 + */ + private final long timeoutDuration; + /** + * 限制刷新周期,即每多少时间刷新一次,单位毫秒 + */ + private final long limitRefreshPeriod; + /** + * 每个周期的许可数 + */ + private final int limitForPeriod; + + /** + * 构造 + * + * @param timeoutDuration 超时时间,即超过这个时间没有获取到许可,则返回false + * @param limitRefreshPeriod 限制刷新周期,即每多少时间刷新一次 + * @param limitForPeriod 个周期的许可数 + */ + public RateLimiterConfig(final long timeoutDuration, final long limitRefreshPeriod, final int limitForPeriod) { + this.timeoutDuration = timeoutDuration; + this.limitRefreshPeriod = limitRefreshPeriod; + this.limitForPeriod = limitForPeriod; + } + + /** + * 超时时间,即超过这个时间没有获取到许可,则返回false,单位毫秒 + * + * @return 超时时间,单位毫秒 + */ + public long getTimeoutDuration() { + return timeoutDuration; + } + + /** + * 限制刷新周期,即每多少时间刷新一次,单位毫秒 + * + * @return 限制刷新周期,单位毫秒 + */ + public long getLimitRefreshPeriod() { + return limitRefreshPeriod; + } + + /** + * 每个周期的许可数 + * + * @return 每个周期的许可数 + */ + public int getLimitForPeriod() { + return limitForPeriod; + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/SemaphoreRateLimiter.java b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/SemaphoreRateLimiter.java new file mode 100644 index 000000000..91a717e38 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/SemaphoreRateLimiter.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024 Hutool Team. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.core.thread.ratelimiter; + +import org.dromara.hutool.core.lang.Assert; +import org.dromara.hutool.core.lang.Opt; +import org.dromara.hutool.core.thread.NamedThreadFactory; + +import java.util.concurrent.*; + +/** + * 基于{@link Semaphore} 实现的限流器
+ * 参考:https://github.com/TFdream/juice/blob/master/juice-ratelimiter/src/main/java/juice/ratelimiter/internal/SemaphoreBasedRateLimiter.java + * + * @author Ricky Fung + * @since 6.0.0 + */ +public class SemaphoreRateLimiter implements RateLimiter { + + private final RateLimiterConfig rateLimiterConfig; + private final ScheduledExecutorService scheduler; + private final Semaphore semaphore; + + /** + * 构造 + * + * @param rateLimiterConfig 限流配置 + */ + public SemaphoreRateLimiter(final RateLimiterConfig rateLimiterConfig) { + this(rateLimiterConfig, null); + } + + /** + * 构造 + * + * @param rateLimiterConfig 限流配置 + * @param semaphore {@link Semaphore} + */ + public SemaphoreRateLimiter(final RateLimiterConfig rateLimiterConfig, final Semaphore semaphore) { + this(rateLimiterConfig, semaphore, null); + } + + /** + * 构造 + * + * @param rateLimiterConfig 限流配置 + * @param semaphore {@link Semaphore} + * @param scheduler 定时器 + */ + public SemaphoreRateLimiter(final RateLimiterConfig rateLimiterConfig, final Semaphore semaphore, final ScheduledExecutorService scheduler) { + this.rateLimiterConfig = Assert.notNull(rateLimiterConfig); + this.semaphore = Opt.ofNullable(semaphore).orElseGet(() -> new Semaphore(rateLimiterConfig.getLimitForPeriod())); + this.scheduler = Opt.ofNullable(scheduler).orElseGet(this::configureScheduler); + //启动定时器 + scheduleLimitRefresh(); + } + + @Override + public boolean tryAcquire(final int permits) { + return semaphore.tryAcquire(permits); + } + + /** + * 刷新限制,填满许可数为{@link RateLimiterConfig#getLimitForPeriod()}
+ * 用户可手动调用此方法填满许可 + * + * @see RateLimiterConfig#getLimitForPeriod() + */ + public void refreshLimit() { + semaphore.release(this.rateLimiterConfig.getLimitForPeriod() - semaphore.availablePermits()); + } + + /** + * 创建定时器 + * + * @return 定时器 + */ + private ScheduledExecutorService configureScheduler() { + final ThreadFactory threadFactory = new NamedThreadFactory("SemaphoreRateLimiterScheduler-", true); + return new ScheduledThreadPoolExecutor(1, threadFactory); + } + + /** + * 启动定时器 + */ + private void scheduleLimitRefresh() { + final long limitRefreshPeriod = this.rateLimiterConfig.getLimitRefreshPeriod(); + scheduler.scheduleAtFixedRate( + this::refreshLimit, + limitRefreshPeriod, + limitRefreshPeriod, + TimeUnit.MILLISECONDS + ); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/package-info.java b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/package-info.java new file mode 100644 index 000000000..ba53d0e14 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/thread/ratelimiter/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024 Hutool Team. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * 限流器实现,几种策略包括: + * + * 概念见:https://www.explainthis.io/zh-hans/swe/rate-limiter + * + * @author Looly + * @since 6.0.0 + */ +package org.dromara.hutool.core.thread.ratelimiter;