令牌桶算法:

生产逻辑:程序以恒定的速率产生令牌,然后把令牌放到令牌桶中,令牌桶有一个容量,当令牌桶满了的时候,无法再向桶中放置令牌;

消费逻辑:当想要处理一个请求的时候,则从令牌桶中取出一个令牌,如果此时令牌桶中没有令牌,那么则拒绝处理请求。

优点:既能限制数据的平均传输速率,又能允许某种程度的突发传输;

算法实现:

引入依赖

        <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.14.1</version></dependency>

令牌桶工具类

import com.google.common.base.Preconditions;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;/*** 按数据库配置对其进行限流*/
@Component
public class RedisRateLimiter {@Autowiredprivate RedissonClient redissonClient;// ratelimiter缓存容器private static Cache<String, Optional<RRateLimiter>> rrLimiterCache = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();/*** 通用法** @param key* @return*/public boolean acquire(String key, int num, int interval) {//acquireRRateLimiter rrLimiter = getRrLimiter("RateLimiter_" + key, num, interval);return rrLimiter.tryAcquire();}/*** 获取流限器** @param key* @param num      令牌数* @param interval 时间窗口大小* @return*/@SneakyThrowsprivate RRateLimiter getRrLimiter(String key, int num, int interval) {//如不存在相应的limiter,进行新建return rrLimiterCache.get(key, new Callable<Optional<RRateLimiter>>() {@Overridepublic Optional<RRateLimiter> call() {RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);rateLimiter.setRate(RateType.OVERALL, num, interval, RateIntervalUnit.SECONDS);//如果不存在,进行放置return Optional.of(rateLimiter);}}).get();}@SneakyThrowspublic RRateLimiter getFowIdLimit(String key) {//如不存在相应的limiter,进行新建return rrLimiterCache.get(key, new Callable<Optional<RRateLimiter>>() {@Overridepublic Optional<RRateLimiter> call() {RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);// 1秒一个rateLimiter.setRate(RateType.OVERALL, 5, 1, RateIntervalUnit.SECONDS);//如果不存在,进行放置return Optional.of(rateLimiter);}}).get();}
}

具体使用:

 // 获取到命名为key的令牌桶RRateLimiter rateLimiter = redisRateLimiter.getFowIdLimit(key);// 判断是否能够取到令牌if (rateLimiter.tryAcquire()) {// 处理请求sendMessage(webHookRequest, flowDefinitions, appID, customerID, eventType);} else {// 拒绝请求saveHookUrl(webHookRequest, flowDefinitions, appID, customerID,eventType, StatusCodeEnum.LIMITED.getCode());}

可以看出,单机环境和分布式限流器的不同在于:对令牌桶的实现方法不同。单机环境下所有的令牌都生成在内存中,是JVM级别的限流;当令牌生产方式变为redis,便做到了分布式环境下的限流。

学习心得:
根据令牌桶算法,桶中的令牌是持续生成存放的,需要先从桶中拿到令牌才能开始执行请求,那么持续生成令牌存放应该这么实现呢?

一般实现:开启一个定时任务,由定时任务持续生成令牌。
缺点:每维护一个令牌桶都需要创建一个任务,会极大的消耗系统资源。如某接口需要分别对每个用户做访问频率限制,假设系统中存在100W用户,则可能需要开启100W个定时任务来维持每个桶的令牌数。

RRateLimiter实现:延迟计算法,具体函数如下。

/*** Updates {@code storedPermits} and {@code nextFreeTicketMicros} based on the current time*/
void resync(long nowMicros) {// if nextFreeTicket is in the past, resync to nowif (nowMicros > nextFreeTicketMicros) {double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();storedPermits = min(maxPermits, storedPermits + newPermits);nextFreeTicketMicros = nowMicros;}
}

基于当前时间,更新下一次请求令牌的时间,以及当前存储的令牌(即生成令牌),这样一来,只需要在获取令牌时计算一次即可。

分布式环境下限流器springboot实现,令牌桶相关推荐

  1. JAVA中useDrlimiter方法_今天来讲讲分布式环境下,怎么达到对象共享,以及实现原子性(atomic),以Redis中的Redisson为例(实现分布式锁、分布式限流等)...

    相信各位对redis肯定是不陌生的,一个高吞吐量的内存型结构存储数据库.可用用于很多业务场景,能够有效的解决很多复杂的并发问题,分布式问题. 下面粘一下中文官网介绍: 关于解决对象共享问题,很多方式, ...

  2. C#session共享+redis_Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享...

    精品推荐 国内稀缺优秀Java全栈课程-Vue+SpringBoot通讯录系统全新发布! Docker快速手上视频教程(无废话版)[免费] 作者:夜月归途 转载自: https://www.cnblo ...

  3. 分布式精华问答:分布式环境下如何保持数据一致性的?| 技术头条

    分布式开发的时代实际上早已悄悄地成为了时代的主流,今天,我们就来看看关于分布式的精华问答吧! 1 Q:分布式系统中主要是用到了服务化,消息中间件,数据库拆分,便于横向扩展和维护,但分布式系统中的拆分的 ...

  4. 面试官:分布式环境下,如何实现session共享

    点击关注公众号,实用技术文章及时了解 先了解一下为什么会出现这种session共享的解决方案? 随着互联网公司的项目在微服务和分布式的环境下进行的搭建,导致一个项目可能分别部署在几个甚至很多的服务器集 ...

  5. 分布式环境下,互斥性与幂等性问题,分析与解决思路

    欢迎关注方志朋的博客,回复"666"获面试宝典 随着互联网信息技术的飞速发展,数据量不断增大,业务逻辑也日趋复杂,对系统的高并发访问.海量数据处理的场景也越来越多.如何用较低成本实 ...

  6. mysql集群session_集群/分布式环境下5种session处理策略

    前言 在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理.如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A.B两台服务器,用户在第一次访问网站时,Ngin ...

  7. eclipse spring mysql,eclipse环境下的springboot框架+mybatis访问MySQL报错空指针

    "/")public classTestController { @RequestMapping("/login")publicString login() { ...

  8. Spring-Redis实现分布式环境下主子域名Session共享

    背景: 之前一直采用通过注解的方式配置Spring环境下的子域名共享,其基本思路是通过将session放入redis中,然后将使用HTTPSESSION更改为使用SpringSession的方式,使得 ...

  9. 10.算法进阶之分布式篇——分布式环境下如何生成唯一ID——UUID

    UUID--全局唯一ID--universally unique identifie. 一般来说常用的基于时间进行排序,因为时间是自然递增的.但是全局唯一ID的两个核心要求是: 全局唯一 粗略有序 在 ...

最新文章

  1. 免息月供137元,新iPhone SE有7大理由值得买!但反对只需这1个就够了
  2. JQuery 动态生成元素添加点击事件
  3. HTML5调用redis,redis实现从数据库获取数据添加到html页面上
  4. Gym - 101972A Multiplication Dilemma(模拟)
  5. Activity的缓存方法
  6. 训练日志 2019.3.10
  7. 比国内贵3000元!小米11 Ultra将于5月11日登陆欧洲市场
  8. 第 16 章 模板方法模式
  9. 搭建开发环境之串口线的选择
  10. Rstudio中修改工作路径的三种方法
  11. 开题报告(3.研究的思路、过程与方法)
  12. PHP资源汇总-内容包括模板、框架、数据库、安全等方面的库和工具
  13. js return加分号_JS代码中加分号和不加分号的区别
  14. 阜阳市计算机学校助学金申请书,计算机专业学生助学金申请书范文
  15. BJTU1935 铁憨憨骑士团的购买装备
  16. python中plot是什么意思_讲述python中ubplot的详细用法
  17. 胡谈编程语言:从C语言到Julia
  18. 基于python中jieba包的中文分词中详细使用(一)
  19. java中cookie的有效时间设置
  20. 浅析C#UDP传输大文件

热门文章

  1. springmvc java.lang.IllegalStateException: getOutputStream() has already been called for this respon
  2. 名称、系统服务-windows系统进程解析 -by小雨
  3. 事务的并发读问题(简图)
  4. 抽象基类与接口,共性与个性的选择!(区别)
  5. 托福口语_新航道_刘莹_task 3 task5
  6. excel如何将内容拆分
  7. 为了牙齿美白,该不该给百天的小宝宝补充氟化物?
  8. 八月暑期福利,10本Python热门书籍免费送!
  9. 台式计算机无线网络连接打印机,台式机怎么样连接无线打印机
  10. 关于开发语言个人随想