限流与令牌桶

39 篇文章 19 订阅
5 篇文章 0 订阅

一、概述

令牌桶是一种常用的流量控制技术。令牌桶本身没有丢弃和优先级策略。

原理

1.令牌以一定的速率放入桶中。

2.每个令牌允许源发送一定数量的比特。

3.发送一个包,流量调节器就要从桶中删除与包大小相等的令牌数。

4.如果没有足够的令牌发送包,这个包就会等待直到有足够的令牌(在整形器的情况下)或者包被丢弃,也有可能被标记更低的DSCP(在策略者的情况下)。

5.桶有特定的容量,如果桶已经满了,新加入的令牌就会被丢弃。因此,在任何时候,源发送到网络上的最大突发数据量与桶的大小成比例。令牌桶允许突发,但是不能超过限制。

二、原理图

在这里插入图片描述

三、令牌桶工具类

简单模拟;
只有生成令牌方法为静态的;

  • TreeSet 桶
package com.tuwer.util;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * <p>令牌桶</p>
 *
 * @author 土味儿
 * @version 1.0
 * @Date 2023/6/14
 */
public class TokenBucket {
    /**
     * 速率(每秒)
     */
    private int rate;
    /**
     * 令牌桶
     */
    private TreeSet<String> bucket;

    public TokenBucket(int rate) {
        this.rate = Math.max(rate, 1);
        this.bucket = new TreeSet<>();
        this.put();
    }

    /**
     * 获取令牌
     *
     * @return
     */
    public boolean getToken() {
        // 从桶中弹出第一个令牌;如果弹出成功为true
        return Objects.nonNull(this.bucket.pollFirst());
    }

    /**
     * 补充令牌
     * 使桶内令牌保持与速率一致
     */
    private void put() {
        // 开启新线程
        Executors.newSingleThreadExecutor().execute(() -> {
            int num;
            // 匀速补充令牌
            while (true) {
                // 需要补充的令牌数量
                num = this.rate - this.bucket.size();
                this.bucket.addAll(createToken(num));

                // 休眼1秒
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 生成num个令牌
     *
     * @param num
     * @return
     */
    private static Set<String> createToken(int num) {
        Set<String> res = new HashSet<>();
        if (num < 1) {
            return res;
        }

        // 当前时间戳
        long second = Instant.now().getEpochSecond();
        for (int i = 1; i < num + 1; i++) {
            res.add(second + "-" + i);
        }
        return res;
    }
}
  • List 桶
package com.tuwer.util;

import java.time.Instant;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * <p>令牌桶</p>
 *
 * @author 土味儿
 * @version 1.0
 * @Date 2023/6/14
 */
public class TokenBucketOfList {
    /**
     * 速率(每秒)
     */
    private int rate;
    /**
     * 令牌桶
     */
    private List<String> bucket;

    public TokenBucketOfList(int rate) {
        this.rate = Math.max(rate, 1);
        this.bucket = new ArrayList<>(this.rate);
        this.put();
    }

    /**
     * 获取令牌
     *
     * @return
     */
    public boolean getToken() {
        if(bucket.isEmpty()){
            return false;
        }
        // 从桶中弹出第一个令牌
        bucket.remove(0);
        return true;
    }

    /**
     * 补充令牌
     * 使桶内令牌保持与速率一致
     */
    private void put() {
        // 开启新线程
        Executors.newSingleThreadExecutor().execute(() -> {
            int num;
            // 匀速补充令牌
            while (true) {
                // 需要补充的令牌数量
                num = this.rate - this.bucket.size();
                this.bucket.addAll(createToken(num));

                // 休眼1秒
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 生成num个令牌
     *
     * @param num
     * @return
     */
    private static Set<String> createToken(int num) {
        Set<String> res = new HashSet<>();
        if (num < 1) {
            return res;
        }

        // 当前时间戳
        long second = Instant.now().getEpochSecond();
        for (int i = 1; i < num + 1; i++) {
            res.add(second + "-" + i);
        }
        return res;
    }
}

四、模拟API接口

1、服务1

每秒可以处理5个请求

package com.tuwer.service;

import com.tuwer.util.TokenBucket;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>服务类</p>
 *
 * @author 土味儿
 * @version 1.0
 * @Date 2023/6/14
 */
public class MyService1 {
    private static TokenBucket tokenBucket = new TokenBucket(5);

    public List<String> userList(){
        if(tokenBucket.getToken()){
            List<String> list = new ArrayList<>();
            list.add("张三");
            list.add("李四");
            list.add("王五");
            list.add("赵六");
            System.out.println(list);
            return list;
        }else{
            System.out.println("\nMyService1 服务忙,请稍候再试!\n");
            return null;
        }
    }
}

2、服务2

每秒可以处理3个请求

package com.tuwer.service;

import com.tuwer.util.TokenBucket;
import com.tuwer.util.TokenBucketOfList;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>服务类</p>
 *
 * @author 土味儿
 * @version 1.0
 * @Date 2023/6/14
 */
public class MyService2 {
    private static TokenBucket tokenBucket = new TokenBucket(3);

    public List<String> userList(){
        if(tokenBucket.getToken()){
            List<String> list = new ArrayList<>();
            list.add("AAA");
            list.add("BBB");
            list.add("CCC");
            System.out.println(list);
            return list;
        }else{
            System.out.println("\nMyService2 服务忙,请稍候再试!\n");
            return null;
        }
    }
}

五、测试

向两个服务分别发送30个请求,每个请求间隔150毫秒

public class MyTest {
    public static void main(String[] args) {
        MyService1 myService1 = new MyService1();
        MyService2 myService2 = new MyService2();
        int num = 30;
        for (int i = 0; i < num; i++) {
            try {
                TimeUnit.MILLISECONDS.sleep(150);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            myService1.userList();
            myService2.userList();
        }
    }
}
  • 服务1:每秒可以处理5个请求;每隔150毫秒到来1个请求,1秒内会有6个请求,第6个请求超出处理能力,会拒绝。每秒钟会向令牌桶中补充令牌(至5个为止),第7个请求会获得新令牌…

在这里插入图片描述

在这里插入图片描述

  • 服务2:每秒可以处理3个请求;每隔150毫秒到来1个请求,1秒内会有6个请求,第4个请求开始超出处理能力,会拒绝。每秒钟会向令牌桶中补充令牌(至3个为止),第7个请求会获得新令牌…

在这里插入图片描述

在这里插入图片描述

  • 服务类中用new的方式生成令牌桶工具类,使各个令牌桶互不影响
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
网关令牌限流是一种常用的流量控制机制,用于保护后端服务免受过多请求的影响。它基于令牌桶算法,通过限制请求的速率来平滑流量,确保系统的稳定性和可靠性。 在网关令牌限流中,令牌桶是一个固定容量的桶,其中包含一定数量的令牌。每个令牌代表一个请求的许可。当有请求到达时,网关会检查桶中是否有足够的令牌。如果有足够的令牌,则请求被允许通过,并从桶中消耗一个令牌;如果没有足够的令牌,则请求被拒绝。 令牌桶算法的特点是可以在短时间内处理突发流量,同时也可以限制平均请求速率。通过调整令牌生成速率和桶的容量,可以灵活地控制系统的吞吐量和并发度。 网关令牌限流的优点包括: 1. 简单有效:实现相对简单,能够有效地控制流量。 2. 平滑限流:通过令牌桶算法,可以平滑地限制请求的速率,避免突发流量对系统造成过大的压力。 3. 灵活配置:可以根据实际需求调整令牌生成速率和桶的容量,以适应不同的业务场景。 然而,网关令牌限流也存在一些限制和注意事项: 1. 令牌桶算法需要一定的计算资源,对于高并发场景需要进行性能测试和优化。 2. 如果请求速率超过了令牌桶的容量,会导致请求被拒绝,可能会影响用户体验。 3. 令牌桶算法只能控制请求的速率,无法对请求的处理时间进行限制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

土味儿~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值