package cn.reghao.tnb.sb.ratelimiter;

import lombok.Getter;
import lombok.Setter;

import java.util.Date;
import java.util.LinkedList;

/**
 * 漏桶算法
 *
 * @author reghao
 * @date 2025-07-15 13:56:41
 */
public class LeakyBucketRateLimiter {
    // 漏斗容量
    private int capacity;
    // 漏斗速率(qps)
    private int rate;
    // 剩余容量
    private int left;
    private LinkedList<Request> requestList;

    public LeakyBucketRateLimiter(int capacity, int rate) {
        this.capacity = capacity;
        this.rate = rate;
        this.left = capacity;
        this.requestList = new LinkedList<>();
        startThread();
    }
    
    private void startThread() {
        // 开启一个定时线程以固定的速率将漏斗中的请求流出并进行处理
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    if (!requestList.isEmpty()) {
                        Request request = requestList.removeFirst();
                        handleRequest(request);
                    }
                    try {
                        Thread.sleep(1000/rate);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    // 处理请求
    private void handleRequest(Request request) {
        request.setHandleTime(new Date());
        System.out.println(request.getCode() +
                "号请求被处理，请求发起时间:" + request.getLaunchTime() +
                ",请求处理时间:" + request.getHandleTime() +
                ",处理耗时:" + (request.getHandleTime().getTime() -
                request.getLaunchTime().getTime()) + "ms");
    }

    public synchronized boolean tryAcquire(Request request) {
        if (left <= 0) {
            return false;
        } else {
            left--;
            requestList.addLast(request);
            return true;
        }
    }

    @Setter
    @Getter
    static class Request {
        private int code;
        private Date launchTime;
        private Date handleTime;

        public Request(int code, Date launchTime) {
            this.launchTime = launchTime;
            this.code = code;
        }
    }

    public static void main(String[] args) {
        LeakyBucketRateLimiter LeakyBucketRateLimiter = new LeakyBucketRateLimiter(5, 2);
        for (int i = 1; i <= 10; i++) {
            Request request = new Request(i, new Date());
            if (LeakyBucketRateLimiter.tryAcquire(request)) {
                System.out.println(i + "号请求被接受");
            } else {
                System.out.println(i + "号请求被拒绝");
            }
        }
    }
}
