Java带宽限速器、Springboot限速器

  • Post author:
  • Post category:java



Java限速器,设计思路


1.假设下载或上传速度上限是: m(KB/s),那么发送一个固定的字节数据[假设是n字节(Bytes)]

时间花费 -> 单位:毫秒 == 1000 * n/(m*1024)

2.假设发送n字节的数据只花费了t毫秒,

那么发送线程就应该睡眠(毫秒): 1000 * n/(m*1024)- t毫秒

java实现代码

public class SpeedLimiter {

    /** 速度上限(KB/s), 0=不限速 */
    private int maxRate = 1024;
    private long getMaxRateBytes(){
        return this.maxRate * 1024;
    }
    private long getLessCountBytes() {
        long lcb = getMaxRateBytes() / 10;
        if (lcb < 10240) lcb = 10240;
        return lcb;
        //return 1024l * 20;
    }
    public SpeedLimiter(int maxRate) {
        this.setMaxRate(maxRate);
    }
    public synchronized void setMaxRate(int maxRate){
        this.maxRate = maxRate < 0 ? 0 : maxRate;
    }
    private long totalBytes = 0;
    private long tmpCountBytes = 0;
    private long lastTime = System.currentTimeMillis();
    /**
     * @author Ethan
     * @date 2022-11-18
     * @param len       send bytes
     * @description 计算线程延时
     * sendTime(Ms) = nowTime - lastTime;
     * workTime(Ms) = (totalBytes*1000)/(maxRate*1024)
     * delayTime(Ms) = workTime-sendTime
     **/
    public synchronized void delayNextBytes(int len) {
        if (maxRate <= 0) return;
        totalBytes += len;
        tmpCountBytes += len;
        //未达到指定字节数跳过...
        if (tmpCountBytes < getLessCountBytes()) {
            return;
        }
        long nowTime = System.currentTimeMillis();
        long sendTime = nowTime - lastTime;
        long workTime = (totalBytes * 1000) / getMaxRateBytes();
        long delayTime = workTime - sendTime;
        if (delayTime > 0) {
            try {
                Thread.sleep(delayTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tmpCountBytes = 0;
        }
    }
}

集成到Springboot进行下载测试

DownloadController代码

@RestController
@RequestMapping(value = "/api/v1/download")
public class DownloadController {

    //设置一个本地文件用于下载测试, 大小约几百MB
    final String localFile = "E:/tmp/A2.tar.gz";

    /**
     * @author Ethan
     * @date 2022-11-18
     * @description 普通下载测试, 不限速!
     **/
    @GetMapping("getFile")
    public void getFile(HttpServletResponse response) {
        File file = this.getLocalFile(response, localFile);
        if (file == null || !file.exists()) {
            return;
        }
        InputStream is = null;
        OutputStream out = null;
        try {
            is = new FileInputStream(file);
            out = response.getOutputStream();
            IOUtils.copyLarge(is, out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(out);
        }
    }

    /**
     * @author Ethan
     * @date 2022-11-18
     * @description 限速下载测试, xxx KB/s
     **/
    @GetMapping("limit")
    public void limit(
            @RequestParam(value = "speed", defaultValue = "500", required = false) int speed,
            HttpServletResponse response
    ) {
        File file = this.getLocalFile(response, localFile);
        if (file == null || !file.exists()) {
            return;
        }
        BufferedInputStream bis = null;
        OutputStream out = null;

        try {
            bis = new BufferedInputStream(new FileInputStream(file));
            out = response.getOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            SpeedLimiter speedLimiter = new SpeedLimiter(speed);
            while ((length = bis.read(buffer)) != -1) {
                out.write(buffer, 0, length);
                if (speedLimiter != null) {
                    speedLimiter.delayNextBytes(length);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(bis);
            IOUtils.closeQuietly(out);
        }
    }

    private File getLocalFile(HttpServletResponse response, String fullPath){
        File file = new File(fullPath);
        if (file.exists()) {
            String fileName = file.getName();
            response.setHeader("Content-Length", String.valueOf(file.length()));
            response.setHeader("Content-Type", "application/x-zip-compressed");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
        } else {
            String noteMsg = "本地测试文件[" + file + "]不存在";
            System.err.println(noteMsg);
            this.setResponse(response, noteMsg);
        }
        return file;
    }
    private final static String CharacterEncoding = "UTF-8";
    private final static String MediaType = "application/json";
    private void setResponse(HttpServletResponse res, String noteMsg) {
        try {
            int httpStatus = HttpStatus.OK.value();
            res.setCharacterEncoding(CharacterEncoding);
            res.setContentType(MediaType);
            res.setStatus(httpStatus);
            res.getWriter().write(noteMsg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


启动Springboot

使用firefox浏览器分别测试

限速下载(500KB/s)   http://127.0.0.1:8080/api/v1/download/limit



限速下载(900KB/s)   http://127.0.0.1:8080/api/v1/download/limit?speed=900

经过测试,可以满足需求!

完整demo下载地址


https://download.csdn.net/download/tianbbs2008/87126141



版权声明:本文为tianbbs2008原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。