用JAVA实现一个简单的预热功能

该博客介绍了一个Java实现的预热控制器,用于在服务调用中动态控制QPS(每秒查询率)。预热控制器根据设定的最大预热时间和最大QPS,逐步增加允许的QPS,以避免对服务器造成过大压力。在预热过程中,如果请求超过当前窗口的QPS限制,控制器将进行限速并等待下一窗口。此外,还提供了详细的代码实现和使用示例。
摘要由CSDN通过智能技术生成

背景

某服务调用,因服务器性能问题,无法直接使用最大qps进行调用,需要动态加速

逻辑

设置最大加速时间,设置允许加速到的最大qps

代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 预热控制器
 * @author chunyang.leng
 * @date 2022-03-11 6:22 PM
 */
public class WarmUpManager {
    private static final Logger logger = LoggerFactory.getLogger(WarmUpManager.class);

    /**
     * 预热总时长,单位秒
     */
    private final Long second;

    /**
     * 允许加速到的最大的qps
     */
    private final Long maxQps;

    /**
     * 描述信息
     */
    private String description;

    /**
     * 允许每秒执行的次数
     */
    private final AtomicLong secondQps;
    /**
     * 执行次数计数器
     */
    private AtomicLong requestCount = new AtomicLong(0);

    /**
     * 当前时间,预热功能使用
     */
    private long currentTimeMillis = System.currentTimeMillis();

    /**
     * 时间窗口ms,固定值,一秒
     */
    private final long interval = 1000;

    /**
     * 初始化时间
     */
    private final long initTime = System.currentTimeMillis();

    /**
     * 步进值,默认10
     */
    private final long warmUpLimitStepValue;

    /**
     * 预热结束时间戳
     */
    private final long endTimeMillis;

    /**
     * 创建预热控制对象
     *
     * @param second 预热总时长,单位:秒
     * @param maxQps 允许加速到的最大的qps,不允许为空
     * @param description 描述信息,用于打印日志
     */
    public WarmUpManager(long second,  long maxQps, String description) {
        if(maxQps <= 0){
            throw new IllegalArgumentException("maxQPs 必须大于0");
        }
        this.second = second;
        this.maxQps = maxQps;

        // 计算每秒允许通过的qps
        secondQps = new AtomicLong(maxQps / second);
        if (secondQps.get() == 0) {
            // 防止为0
            secondQps.set(1);
        }
        // 设置步进
        warmUpLimitStepValue = secondQps.get();
        if(description == null){
            this.description = "";
        }
        // 计算预热结束时间
        endTimeMillis = currentTimeMillis + second * interval;
    }


    /**
     * 开始预热
     *
     */
    public void warmUp() throws InterruptedException {
        // 当前时间戳
        long now = System.currentTimeMillis();

        if (now >= endTimeMillis ) {
            // 当前时间大于等于结束时间,预热结束
            return;
        }
        // 计算下一跳窗口起始时间戳
        long total = currentTimeMillis + interval;

        if ((now < total) && requestCount.incrementAndGet() > secondQps.get()) {
            // 当前时间小于下一窗口开始时间,并且请求次数大于限制的qps数,需要进行限速

            // ((now - initTime) / 1000) + 1 ,是因为开始时间为0
            // 日志secondQps.get() + 1 ,是因为 secondQps.getAndAdd(warmUpLimitStepValue); 会丢失1
            LogUtils.info(logger, "{} 预热中,已预热{}秒,qps限流:{}", description, ((now - initTime) / 1000) + 1, secondQps.get() + 1);

            // 执行sleep,睡眠时间为,下一跳开始时间 到 当前时间差
            Thread.sleep(total - now);

            currentTimeMillis = System.currentTimeMillis();
            // 超时后重置
            requestCount = new AtomicLong(0);

            // 重新计算qps大小,步进累加,且不能超出最大限制
            if (secondQps.get() + warmUpLimitStepValue < maxQps) {
                secondQps.getAndAdd(warmUpLimitStepValue);
            }
        }
    }
}


使用方式

public class Test {


    public static void main(String[] args) throws InterruptedException {
        WarmUpManager warmUpManager = new WarmUpManager(10L,5L,"");

        int max = 1000;
        for (int i = 0; i < max; i++) {
            warmUpManager.warmUp();
            System.out.println("==========> " + (  i));
        }
        System.out.println("====run done ");
    }

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北漂的菜小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值