guava ratelimiter限流(2)

java通过guava工具实现限流 引入jar包 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1-jre</version> </dependency> 限流测试 @RestController @RequestMapping("/test") public class TestCon 阅读详情

【订阅专栏合集,关注公众号,作者所有付费文章都能看(持续更新)】

  本篇是《Guava RateLimiter互联网限流实战(上)》的姊妹篇,主要介紹算法java实现。包括计数器法、滑动窗口计数法、漏斗桶算法、令牌桶算法。

Guava ratelimiter工程概览

在这里插入图片描述

基于redis的简单计数法

新建springboot工程并引入依赖

<properties>
    <java.version>1.8</java.version>
    <spring.version>2.3.1.RELEASE</spring.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.8.0</version>
    </dependency>

    <dependency>
        <groupId>io.lettuce</groupId>
        <artifactId>lettuce-core</artifactId>
        <version>5.3.2.RELEASE</version>
    </dependency>
</dependencies>

配置application.properties

server.port=8888
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=20
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=1000
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=2000

编写RedisCountLimit

基于redis的incr机制

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalTime;
import java.util.concurrent.TimeUnit;

/**
 * 计数法限流
 */
@Component
public class RedisCountLimit {
    public static final String KEY = "ratelimit_";
    public static final int LIMIT = 10;

    @Autowired
    StringRedisTemplate redisTemplate;

    public boolean triggerLimit(String reqPath) {
        String redisKey = KEY + reqPath;
        Long count = redisTemplate.opsForValue().increment(redisKey, 1);
        System.out.println(LocalTime.now() + " " + reqPath + " " + count);
        if (count != null && count == 1) {
            redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);
        }
        //防止出现并发操作未设置超时时间的场景,这样key就是永不过期,存在风险
        if (redisTemplate.getExpire(redisKey, TimeUnit.SECONDS) == -1) {
            redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);
        }

        if (count > LIMIT) {
            System.out.println(LocalTime.now() + " " + reqPath + " count is:" + count + ",触发限流");
            return true;
        }

        return false;
    }
}

Controller层集成

import com.bigbird.ratelimit.rediscount.RedisCountLimit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 基于redis的计数器限流demo
 */
@RestController
public class RedisCountLimitController {
    @Autowired
    RedisCountLimit redisCountLimit;

    @RequestMapping("/rediscount")
    public String redisCount(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = redisCountLimit.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }

    @RequestMapping("/rediscount2")
    public String redisCount2(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = redisCountLimit.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }
}

运行测试

启动springboot工程,确保redis已运行,浏览器访问,f5多刷新几次

  • http://localhost:8888/rediscount
  • http://localhost:8888/rediscount2

基于redis的滑动窗口计数法

编写RedisSlidingCountLimit

通过redis的zset数据结构

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalTime;
import java.util.UUID;

/**
 * 滑动窗口计数法限流
 */
@Component
public class RedisSlidingCountLimit {
    public static final String KEY = "slidelimit_";
    public static final int LIMIT = 10;
    //限流时间间隔(秒)
    public static final int PERIOD = 60;

    @Autowired
    StringRedisTemplate redisTemplate;

    public boolean triggerLimit(String reqPath) {
        String redisKey = KEY + reqPath;

        if (redisTemplate.hasKey(redisKey)) {
            Integer count = redisTemplate.opsForZSet().rangeByScore(redisKey, System.currentTimeMillis() - PERIOD * 1000, System.currentTimeMillis()).size();
            System.out.println(count);
            if (count != null && count > LIMIT) {
                System.out.println(LocalTime.now() + " " + reqPath + " count is:" + count + ",触发限流");
                return true;
            }
        }

        long currentTime = System.currentTimeMillis();
        redisTemplate.opsForZSet().add(redisKey, UUID.randomUUID().toString(), currentTime);
        // 清除旧的访问数据,比如period=60s时,标识清除60s以前的记录
        redisTemplate.opsForZSet().removeRangeByScore(redisKey, 0, System.currentTimeMillis() - PERIOD * 1000);
        return false;
    }
}

Controller层集成

import com.bigbird.ratelimit.rediscount.RedisSlidingCountLimit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 基于Redis的滑动窗口计数器限流demo
 */
@RestController
public class RedisSlidingCountLimitController {
    @Autowired
    RedisSlidingCountLimit redisSlidingCountLimit;

    @RequestMapping("/slidecount")
    public String redisCount(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = redisSlidingCountLimit.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }

    @RequestMapping("/slidecount2")
    public String redisCount2(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = redisSlidingCountLimit.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }
}

运行测试

启动springboot工程,确保redis已运行,访问

  • http://localhost:8888/slidecount
  • http://localhost:8888/slidecount2

漏斗桶算法

编写LeakyBucket

import java.time.LocalTime;

/**
 * 漏斗桶算法限流
 */
public class LeakyBucket {
    /**
     * 每秒处理数量(出水速率)
     */
    private int rate;

    /**
     * 桶容量
     */
    private int capacity;

    /**
     * 当前水量
     */
    private int water;

    /**
     * 最后刷新时间
     */
    private long refreshTime;

    public LeakyBucket(int rate, int capacity) {
        this.capacity = capacity;
        this.rate = rate;
    }

    private void refreshWater() {
        long now = System.currentTimeMillis();
        water = (int) Math.max(0, water - (now - refreshTime) / 1000 * rate);
        refreshTime = now;
    }

    public synchronized boolean triggerLimit(String reqPath) {
        refreshWater();
        if (water < capacity) {
            water++;
            System.out.println(LocalTime.now() + " " + reqPath + " current capacity is:" + (capacity - water) + ",water is:" + water + ",请求成功");
            return false;
        } else {
            System.out.println(LocalTime.now() + " " + reqPath + " current capacity is:" + (capacity - water) + ",water is:" + water + ",触发限流");
            return true;
        }
    }
}

Controller层集成

import com.bigbird.ratelimit.leakybucket.LeakyBucket;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 漏斗桶算法限流demo
 */
@RestController
public class LeakyBucketLimitController {
    LeakyBucket bucket1 = new LeakyBucket(2, 10);
    LeakyBucket bucket2 = new LeakyBucket(2, 20);

    @RequestMapping("/leakyBucket1")
    public String leakyBucket1(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = bucket1.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }

    @RequestMapping("/leakyBucket2")
    public String leakyBucket2(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = bucket2.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }
}

运行测试

启动springboot工程,浏览器访问下列地址,连续f5多刷新测试

  • http://localhost:8888/leakyBucket1
  • http://localhost:8888/leakyBucket2

令牌桶算法(RateLimiter)

基于Guava RateLimiter实现

引入依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>

编写TokenBucket

import com.google.common.util.concurrent.RateLimiter;
import java.time.LocalTime;
import java.util.concurrent.TimeUnit;

/**
 * 令牌桶算法限流
 */
public class TokenBucket {
    /**
     * qps,即每秒处理数量
     */
    private int rate;
    private RateLimiter rateLimiter;

    public TokenBucket(int rate) {
        this.rate = rate;
        this.rateLimiter = RateLimiter.create(rate);
        //在实际业务开发中,一般一种限流场景下的个体对应一个RateLimiter实例
        //比如对客户端IP限流,会创建一个 static volatile 的 Map <Ip,RateLimiter>保存各个IP的限流器
        //比如对url限流,会创建一个 static volatile 的 Map <Url,RateLimiter>保存各个Url的限流器
        //比如对商户限流,会创建一个 static volatile 的 Map <MerchantId,RateLimiter>保存各个商户的限流器
        //static volatile 修饰的变量可以保证全局可见、统一配置,实时修改限流配置后立即生效
    }

    public boolean triggerLimit(String reqPath) {
        boolean acquireRes = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);
        if (acquireRes) {
            System.out.println(LocalTime.now() + " " + reqPath + ",请求成功");
            return false;
        } else {
            System.out.println(LocalTime.now() + " " + reqPath + ",触发限流");
            return true;
        }
    }
}

关于volatile关键字参考《volatile关键字解析与实践》

Controller层集成

import com.bigbird.ratelimit.tokenbucket.TokenBucket;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

/**
 * 令牌桶限流算法demo
 */
@RestController
public class TokenBucketLimitController {
    /**
     * 每秒钟限速1
     */
    TokenBucket bucket1 = new TokenBucket(1);
    /**
     * 每秒钟限速2
     */
    TokenBucket bucket2 = new TokenBucket(2);

    @RequestMapping("/tokenBucket1")
    public String leakyBucket1(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = bucket1.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }

    @RequestMapping("/tokenBucket2")
    public String leakyBucket2(HttpServletRequest request) {
        String servletPath = request.getServletPath();
        boolean triggerLimit = bucket2.triggerLimit(servletPath);
        if (triggerLimit) {
            return LocalDateTime.now() + " " + servletPath + " 系统忙,稍后再试";
        } else {
            return LocalDateTime.now() + " " + servletPath + "请求成功";
        }
    }
}

运行测试

启动springboot工程,浏览器访问下列地址,连续f5多刷新测试

  • http://localhost:8888/tokenBucket1
  • http://localhost:8888/tokenBucket2

在实际业务开发中,一般一种限流场景下的个体对应一个RateLimiter实例

比如对客户端IP限流,会创建一个 static volatile 的 Map <Ip,RateLimiter>保存各个IP的限流器

比如对接口url限流,会创建一个 static volatile 的 Map <Url,RateLimiter>保存各个Url的限流器

比如对商户限流,会创建一个 static volatile 的 Map <MerchantId,RateLimiter>保存各个商户的限流器

static volatile 修饰的变量可以保证全局统一配置,实时修改限流配置后立即生效

自定义注解、aop封装限流

  上述实现方式简单粗暴,实际应用中可以封装自定义注解,并通过aop实现controller层接口自动限流拦截。废话不多说,上代码。下面的案例基于RateLimiter令牌桶。其它算法读者可以参考此例自行封装。

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

编写自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRateLimiter {
    double permitsPerSecond();

    long timeout();
}

编写aop切面

import com.bigbird.ratelimit.annotation.ExtRateLimiter;
import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.time.LocalTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 封装基于RateLimiter的限流注解
 */
@Component
@Aspect
public class RateLimiterAop {

    /**
     * 保存接口路径和限流器的对应关系
     */
    private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap();

    @Pointcut("execution(public * com.bigbird.ratelimit.controller.*.*(..))")
    public void rateLimiterAop() {
    }

    /**
     * 使用环绕通知拦截所有Controller请求
     *
     * @param proceedingJoinPoint
     * @return
     */
    @Around("rateLimiterAop()")
    public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        if (method == null) {
            return null;
        }

        ExtRateLimiter extRateLimiter = method.getDeclaredAnnotation(ExtRateLimiter.class);
        if (extRateLimiter == null) {
            return proceedingJoinPoint.proceed();
        }

        double permitsPerSecond = extRateLimiter.permitsPerSecond();
        long timeout = extRateLimiter.timeout();

        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String requestURI = requestAttributes.getRequest().getRequestURI();
        RateLimiter rateLimiter = rateLimiters.get(requestURI);
        if (rateLimiter == null) {
            rateLimiter = RateLimiter.create(permitsPerSecond);
            RateLimiter rateLimiterPrevious = rateLimiters.putIfAbsent(requestURI, rateLimiter);
            if (rateLimiterPrevious != null) {
                rateLimiter = rateLimiterPrevious;
            }
        }

        boolean tryAcquire = rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);
        if (!tryAcquire) {
            System.out.println(LocalTime.now() + " " + requestURI + " 触发限流");
            doFallback();
            return null;
        }

        System.out.println(LocalTime.now() + " " + requestURI + " 请求成功");
        return proceedingJoinPoint.proceed();
    }

    private void doFallback() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = requestAttributes.getResponse();
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.println("系统忙,请稍后再试!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }
}

Controller层集成

对要限流的接口加ExtRateLimiter 注解设置

import com.bigbird.ratelimit.annotation.ExtRateLimiter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalTime;

/**
 * 自定义注解标识接口进行限流
 */
@RestController
public class ExtRateLimiterController {
    @RequestMapping("/extRate1")
    @ExtRateLimiter(permitsPerSecond = 0.5, timeout = 500)
    public String extRate1(HttpServletRequest request) {
        return LocalTime.now() + " " + request.getRequestURI() + "请求成功";
    }

    @RequestMapping("/extRate2")
    @ExtRateLimiter(permitsPerSecond = 2, timeout = 500)
    public String extRate2(HttpServletRequest request) {
        return LocalTime.now() + " " + request.getRequestURI() + "请求成功";
    }
}

运行测试

启动springboot工程,浏览器访问下列地址,连续f5多刷新测试

  • http://localhost:8888/extRate1
  • http://localhost:8888/extRate2

小结

  本文通俗易懂地介绍了互联网限流相关的概念与算法,并且附以Java代码实现。包括计数器法、滑动窗口计数法、漏斗桶算法、令牌桶算法。最后封装了一个自定义限流注解以及aop拦截接口限流。

代码下载地址:https://github.com/bigbirditedu/learn-ratelimit

一桶令牌的时间账本:四类限流算法的边界推演与Guava RateLimiter源码精读 限流的难点不在背出算法名,而在算清每一笔账:按哪一秒计数、超额请求排队还是拒绝、空闲后能否突发、集群总量如何约束。本文在可复现的虚拟时间实验台上对比固定窗口、滑动窗口、漏桶与令牌桶,实测边界双倍突发与分桶滑动的真实上界,拆解Nginx limit_req的千分位记账与延迟机制,纠正Resilience4j常被误称漏桶的说法,并逐行精读Guava SmoothRateLimiter的预消费、懒惰补账与预热积分,复刻迷你限流器与其完成七万次零差异对账,最后给出集群配额、分层防护与参数确定的方法。 阅读详情

相关推荐

Pipeline更多理解

Pipe 类型用途VIGVideo Image Generator:用于图层显示,支持 scaling、rotationRGB简单图层显示,较低优先级DMA主要用于 writeback / WFD / composition output,即从 framebuffer 输出到外部设备(或 memory)Cursor小图层,用于显示指针等很可能是:分别用于主屏、仪表、虚拟显示的输出路径或者用于不同的 display 去做 GPU_TARGET / writeback 的拷贝你看到的意思。

Damon_X的博客 1053

Guava官方文档-RateLimiter类

Guava官方文档-RateLimiter类 原文链接作者:Dimitris Andreou 译者:魏嘉鹏 校对:方腾飞 RateLimiter 从概念上来讲,速率限制器会在可配置的速率下分配许可证。如果必要的话,每个acquire()会阻塞当前线程直到许可证可用后获取该许可证。一旦获取到许可证,不需要再释放许可证。 校对注:RateLimiter使用的是一种叫令牌桶的流控算法,RateLimiter会按照一定的频率往桶里扔令牌,线程拿到令牌才能执行,比如你希望自己的应用程序QPS不要超过1...

java_2017_csdn的博客 2491

易智联LM401模块学习(四)——双串口收发

串口的发送和接受有三种方式,分别是阻塞发送和接收(轮询)、中断方式发送和接收、DMA方式发送和接收。这里我使用阻塞+中断的方式实现双串口通信。简单的串口收发。

weixin_46046597的博客 991

Guava教程-RateLimiter

简介 Google Guava library中提供了RateLimiter类,它经常用于限制对一些物理资源或者逻辑资源的访问速率。与Semaphore 相比,Semaphore 限制了并发访问的数量而不是使用速率。 RateLimiter类定义如下: com.google.common.util.concurrent.RateLimiter @ThreadSafe @Beta public abstract class RateLimiter extends ObjectGuava Docs关于Ra

song_myth的博客 904

grafana 配置页面告警

Rate 下拉选择也可,保持默认也可。By label 选择一个比如选择filename。配置无数据或者错误数据的时候如何处理,这里选择 Normal (表示当做正常不会告警)单机 node_name 标签,选择一个主机,选好后单机 Show logs。单机 New floder,如果之前配置过,也可以选择。单机 Save contact point 保存即可。会跳出如下对话框(如果没有跳出就自己单机选择下)完成后会自动生成如下图,告警规则部分就配置好了。选择告警接收方式(这里选择企业微信 WeCon)

weixin_40548182的博客 3252

达梦DM8整合Seata AT模式实战:手把手教你搞定分布式事务(含源码修改避坑)

本文详细介绍了达梦DM8数据库与Seata AT模式的深度整合实战,包括环境准备、架构设计、源码适配及分布式事务落地。通过独创性源码改造,解决了Seata官方对达梦数据库AT模式支持的限制,为国产数据库在微服务架构中的应用提供了可靠的事务保障。

weixin_30764771的博客 333

google的RateLimiter限流器的使用

背景:A系统需要调用B,C,D系统,B,C,D系统没有能力做限流,因此需要A系统针对B,C,D系统做限流,每秒发送对象要求的请求数。可以使用下面的组件进行控制 import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.util.concu...

红豆和绿豆的博客 5937

别再硬编码了!Flowable工作流动态审批人配置全解析(Spring Boot集成版)

本文深入解析Flowable工作流在Spring Boot中的动态审批人配置技术,通过角色、部门等变量实现审批流程的灵活设计。文章详细介绍了架构原理、前端配置映射、动态候选组解析及查询优化方案,帮助企业提升审批流程的适应性和维护效率。

weixin_30919571的博客 136

基于Redis组件之分布式RateLimiter限流

上文我们介绍了RateLimiter文章路径针对IP来限流的方式,上文的限流方案,只针对单应用情况,分布式集群下就不能使用上文的方式,分布式下的限流方案有很多种,这边展示的是Redis的封装redission框架。 可以这么讲,jdk中的juc包提供的是单机版的并发业务。那么Redisson基本是基于juc实现的分布式的业务。 一:Redission官网 我们先去Redission官网喵喵 redission官方地址 我们可以看到wiki提供了很多功能介绍, 分布式锁等,我们这篇文章主要讲限流。进入正题

BugRoot的博客 9468

智能制造之物料详解

制造业业务系统中的物料流转贯穿需求、采购、生产到交付全流程,各系统协同实现精准追踪。需求阶段由CRM/ERP/PLM生成物料计划;采购入库阶段SCM/WMS/QMS完成采购质检;生产阶段MES/APS协调领料加工;成品交付阶段WMS/SCM处理出库物流。整个流程以BOM为蓝图,ERP为调度中心,通过数据联动实现物料状态、位置和数量的全链路可追溯。

cdfunlove的博客 655

【TC3xx芯片】TC3xx芯片UCB详解

TC3xx芯片UCB详解

qq_36056498的博客 1万+

【Guava】使用Guava的RateLimiter做限流

https://blog.youkuaiyun.com/aa1215018028/article/details/80866335

靡不有初,鲜克有终。 374

【49】软考软件设计师——冲刺复盘:高频易错点与应试策略|复盘清单+应试技巧

本文是《软件设计师·50讲通关|从零基础到工程师职称》专栏第49篇,属于**模块六:冲刺与模拟**终篇,聚焦考前最后阶段的“精准复盘+应试提分”。全文围绕“高频易错点清零、答题时间优化、应急处理落地”三大核心,梳理9大知识领域的典型易错点(附避坑技巧)、优化版答题时间分配(含取舍策略)、考中全场景应急方案,配套可直接使用的“复盘清单”和“应试技巧手册”,帮助考生在最后阶段查漏补缺、固化得分逻辑,避免因低级错误丢分,以“稳、准、快”的状态应对考试。下一篇第50篇(终章)将进行全专栏知识体系回顾、考试策略总结

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 440

微服务架构实战篇(五):Spring boot2.x + Guava 并使用RateLimiter实现秒杀限流demo...

简介 该项目主要利用Spring boot2.x + Guava 实现数据缓存,并使用RateLimiter做秒杀限流示例。 Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库。这个库是为了方便编码,并减少编码错误。这个库提供用于集合,缓存,支持原语,并...

394

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

微服务架构实战篇(五):Spring boot2.x + Guava 并使用RateLimiter实现秒杀限流demo

简介 该项目主要利用Spring boot2.x + Guava 实现数据缓存,并使用RateLimiter做秒杀限流示例。 Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库。这个库是为了方便编码,并减少编码错误。这个库提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方法。 Guava - RateLimiter使用的是一...

zhenghhgz的博客 4231

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

2年开发经验的同事限流玩得6哦,一问才知道用的Guava RateLimiter

Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率,咋一听有点像java并发包下的Samephore,但是又不相同,RateLimiter控制的是速率,Samepho...

独行侠梦的博客 123

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

使用自定义注解+AOP实现RateLimiter令牌桶限流

Springboot 2.x + AOP + RateLimiter 1. 添加依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> &lt...

unclecoco的博客 1258

高并发限流Guava RateLimiter

令牌桶算法是定时发放令牌,请求能够从桶里取出令牌,才能通过限流器。

qq_49739667的博客 439

数据推送限流操作

/ 1. 引入 Guava// 2. 在类中加上// 每秒约5.55条// 3. 在每次发送前加。

XF的博客 410

使用Guava RateLimiter 轻松实现请求第三方接口限流

springboot + guava rateLimiter 实现请求接口限流

smlie_shuihan的博客 881

4种常见的限流算法

当有请求到来时,会尝试从桶中移除一个令牌,如果桶中有足够的令牌,则请求可以被处理或数据包可以被发送;由于漏桶算法以固定的速率处理请求,可以有效地平滑和整形流量,避免流量的突发和波动(类似于消息队列的削峰填谷的作用)。桶中的令牌数不能超过桶的容量,如果新生成的令牌超过了桶的容量,那么新的令牌会被丢弃。例如:限流是每秒3个,在第一毫秒发送了3个请求,达到限流,窗口剩余时间的请求都将会被拒绝,体验不好。在一个固定长度的时间窗口内限制请求数量,每来一个请求,请求次数加一,如果请求数量超过最大限制,就拒绝该请求。

m0_37635053的博客 1141

google guava实现接口限流

guava实现接口限流

wyz_New的博客 310

Java实现限流的工具类

工具类代码: import com.google.common.util.concurrent.RateLimiter; import java.util.concurrent.ConcurrentHashMap; /** * 限流工具类 */ public class RateLimiterManager { private static ConcurrentHashMap<String, RateLimiter> manager = new ConcurrentHash

知世故而不世故 678

【高并发系列】16、Guava的RateLimiter限流

限流的必要性:任何应用或模块组件中,如果请求速率超过了访问速率上限,多余的请求无法处理,严重时可能压垮系统导致所有请求都无法有效处理; 简单限流算法:在给定单位时间内,使用计数器统计收到的请求数量,超过上限时,多余的请求丢弃或等待;这种算法有问题,很难控制边界时间上的请求;假如单位时间t内请求上限为n,0~t/2时间内无请求,t/2~t请求n,t~3t/2请求n,3t/2~2t无请求,分别在0~...

hellboy0621的博客 646

Guava家族

【代码】Guava家族。

u014303746的专栏 312

令牌桶算法-高性能限流器Guava Rate

高性能限流器Guava RateLimiter 我们来看看 Guava RateLimiter 是如何解决高并发场景下的限流问题的。 限流怎么理解呢?(我们创建一个流速为2个请求/秒的限流器) 直观地看,2个请求/秒就是每秒最多允许两个请求通过限流器。 在Guava中,2个请求/秒==一个请求500毫秒 首先我们看看RateLimiter的使用 //限流器流速:2个请求/秒 RateLimiter...

邓新的博客 829
上一篇: guava ratelimiter限流(1)
下一篇: Vuex详细教程
程猿薇茑
程猿薇茑 优质创作者: Java技术领域 优质创作者: Java技术领域 领域专家: 后端开发技术领域 领域专家: 后端开发技术领域
博客等级 码龄16年 6050粉丝 213原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

程猿薇茑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值