Java客户端流控

一、JAVA实现QPS控制

方法1:解决方案 (假设需要控制在1s内不多于400个请求):

方案描述: 
使用数组存储每个请求到来的时间,前400次请求顺利通过,并填满数组。 
后续请求到来时,判断当前时间是否比数组中最早的时间晚1s,未晚,则打回, 
晚则替换数组中最早的值。循环。

参见:https://blog.youkuaiyun.com/u012104435/article/details/50963698

package com.example.demo.util;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.concurrent.TimeUnit;

public class QpsUtil {

    /**
     * 接受请求窗口
     */
    private long[] accessWindow;
    /**
     * 窗口大小
     */
    private int limit;
    /**
     * 指针位置
     */
    private int curPosition;
    /**
     * 时间间隔
     */
    private long period;

    private final Object lock = new Object();

    /**
     * 1秒内最多400次请求
     * @param limit    限制次数  400
     * @param period   时间间隔  1
     * @param timeUnit 间隔类型  秒
     */
    public QpsUtil(int limit, int period, TimeUnit timeUnit) {
        if (limit < 0) {
            throw new IllegalArgumentException("Illegal Capacity: " + limit);
        }
        curPosition = 0;
        this.period = timeUnit.toMillis(period);
        this.limit = limit;
        accessWindow = new long[limit];
        Arrays.fill(accessWindow, 0);
    }

    public boolean isPass() {
        long curTime = System.currentTimeMillis();
        synchronized (lock) {
            if (curTime >= period + accessWindow[curPosition]) {
                accessWindow[curPosition++] = curTime;
                curPosition = curPosition % limit;
                return true;
            } else {
                return false;
            }
        }
    }



    public static void main(String[] args) {
        QpsUtil util =  new QpsUtil(2,1,TimeUnit.SECONDS);
        for(;;){
            if (util.isPass()){
                new Token().run();
            }
        }
    }
}

class  Token  {


    public String run() {
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        System.out.println("server received"+sdf.format(new Date()));
        return  "reponse";
    }
}

方法2:


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 流量控制<br>
 * 根据给定的入参,来控制调用allocate的次数.

 */
public class BandwidthAllocator
{
    /**
     * Interval between allocations, in milliseconds.
     */
    private static final int INTERVAL = 1000;

    /**
     * 对象锁
     */
    private final Lock lock = new ReentrantLock();

    /**
     * timestamp
     */
    private long timestamp;

    /**
     * 最大流量
     */
    private int maxbps;

    private int currentbps;

    private int bytesread;

    /**
     * Creates a new <code>BandwidthAllocator</code> <br>
     * that will allow a specific number of times per second to be transferred.
     *
     * @param bandwidth the maximum number of times per second which should be transmitted
     */
    public BandwidthAllocator(int bandwidth)
    {
        this.maxbps = bandwidth;
        this.currentbps = 0;
        this.bytesread = 0;
        this.timestamp = System.currentTimeMillis();
    }

    /**
     * 流量控制
     */
    public void allocate()
    {
        lock.lock();
        try
        {
            int avaliable = check();
            if (avaliable == 0)
            {
                waitForAvailable();
                avaliable = check();
            }
            update(1);
        }
        finally
        {
            lock.unlock();
        }
    }

    private void waitForAvailable()
    {
        long time = System.currentTimeMillis() - timestamp;
        boolean isInterrupted = false;
        while (time < INTERVAL)
        {
            try
            {
                Thread.sleep(INTERVAL - time);
            }
            catch (InterruptedException e)
            {
                isInterrupted = true;
                break;
            }
            time = System.currentTimeMillis() - timestamp;
        }
        if (isInterrupted)
        {
            Thread.currentThread().interrupt();
        }
    }

    private int check()
    {
        long now = System.currentTimeMillis();
        if (now - timestamp >= INTERVAL)
        {
            timestamp = now;
            currentbps = bytesread;
            bytesread = 0;
            return maxbps;
        }
        else
        {
            return maxbps - bytesread;
        }
    }

    private void update(int n)
    {
        bytesread += n;
    }

    /**
     * 获取当前当前的流速
     * @return  流速
     */
    public int getCurrentbps()
    {
        return currentbps;
    }

    /**
     * {@inheritDoc}
     */
    public String toString()
    {
        return "CurTPS:" + currentbps + ",MaxTPS:" + maxbps;
    }
}
 

 

调用:

        // 初始化流量
        BandwidthAllocator locator = new BandwidthAllocator(contral);

   List<***** > list = ***** (bean);
                for (***** : list)
                {
                    locator.allocate();
                   ... 外部调用

                  ...
                }

2、JAVA实现并发的流控

利用Semaphore实现

可参考 https://blog.youkuaiyun.com/xiangwanpeng/article/details/79728227

 

这个参考 https://blog.youkuaiyun.com/Zhenxue_Xu/article/details/89293037 没看懂

https://blog.youkuaiyun.com/scherrer/article/details/51867873

https://www.cnblogs.com/simoncook/p/10639188.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值