限流_限流电路 - CSDN
精华内容
参与话题
  • 什么是限流及如何限流

    千次阅读 2019-01-02 17:30:39
    此时你需要使用的技术手段之一就是限流,当请求达到一定的并发数或速率,就进行等待、排队、降级、拒绝服务等。在限流时,常见的两种算法是漏桶和令牌桶算法算法。 限流算法 令牌桶(Token Bucket)、漏桶(leaky ...

    概要

    在大数据量高并发访问时,经常会出现服务或接口面对暴涨的请求而不可用的情况,甚至引发连锁反映导致整个系统崩溃。此时你需要使用的技术手段之一就是限流,当请求达到一定的并发数或速率,就进行等待、排队、降级、拒绝服务等。在限流时,常见的两种算法是漏桶和令牌桶算法算法。

    限流算法

    令牌桶(Token Bucket)、漏桶(leaky bucket)和计数器算法是最常用的三种限流的算法。

    1. 令牌桶算法

     

     

    令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。 当桶满时,新添加的令牌被丢弃或拒绝。

    令牌桶算法示例

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    public class RateLimiterDemo {

        private static RateLimiter limiter = RateLimiter.create(5);

     

        public static void exec() {

            limiter.acquire(1);

            try {

                // 处理核心逻辑

                TimeUnit.SECONDS.sleep(1);

                System.out.println("--" + System.currentTimeMillis() / 1000);

            catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

    Guava RateLimiter 提供了令牌桶算法可用于平滑突发限流策略。
    该示例为每秒中产生5个令牌,每200毫秒会产生一个令牌。
    limiter.acquire() 表示消费一个令牌。当桶中有足够的令牌时,则直接返回0,否则阻塞,直到有可用的令牌数才返回,返回的值为阻塞的时间。

    2. 漏桶算法

     

     

    它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量,数据可以以任意速度流入到漏桶中。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。 漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶为空,则不需要流出水滴,如果漏桶(包缓存)溢出,那么水滴会被溢出丢弃。

    3. 计数器限流算法

    计数器限流算法也是比较常用的,主要用来限制总并发数,比如数据库连接池大小、线程池大小、程序访问并发数等都是使用计数器算法。

    使用计数器限流示例1

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    public class CountRateLimiterDemo1 {

     

        private static AtomicInteger count = new AtomicInteger(0);

     

        public static void exec() {

            if (count.get() >= 5) {

                System.out.println("请求用户过多,请稍后在试!"+System.currentTimeMillis()/1000);

            else {

                count.incrementAndGet();

                try {

                    //处理核心逻辑

                    TimeUnit.SECONDS.sleep(1);

                    System.out.println("--"+System.currentTimeMillis()/1000);

                catch (InterruptedException e) {

                    e.printStackTrace();

                finally {

                    count.decrementAndGet();

                }

            }

        }

    }

    使用AomicInteger来进行统计当前正在并发执行的次数,如果超过域值就简单粗暴的直接响应给用户,说明系统繁忙,请稍后再试或其它跟业务相关的信息。

    弊端:使用 AomicInteger 简单粗暴超过域值就拒绝请求,可能只是瞬时的请求量高,也会拒绝请求。

    使用计数器限流示例2

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    public class CountRateLimiterDemo2 {

     

        private static Semaphore semphore = new Semaphore(5);

     

        public static void exec() {

            if(semphore.getQueueLength()>100){

                System.out.println("当前等待排队的任务数大于100,请稍候再试...");

            }

            try {

                semphore.acquire();

                // 处理核心逻辑

                TimeUnit.SECONDS.sleep(1);

                System.out.println("--" + System.currentTimeMillis() / 1000);

            catch (InterruptedException e) {

                e.printStackTrace();

            finally {

                semphore.release();

            }

        }

    }

    使用Semaphore信号量来控制并发执行的次数,如果超过域值信号量,则进入阻塞队列中排队等待获取信号量进行执行。如果阻塞队列中排队的请求过多超出系统处理能力,则可以在拒绝请求。

    相对Atomic优点:如果是瞬时的高并发,可以使请求在阻塞队列中排队,而不是马上拒绝请求,从而达到一个流量削峰的目的。

    展开全文
  • 限流实现与解决方案

    千次阅读 2019-01-27 20:18:57
    一、限流操作: 为什么限流,是防止用户恶意刷新接口,因为部署在外部服务器,并且我们采用websocket的接口实现的,公司没有对硬件升级,导致程序时长崩溃,为了解决这个问题,请教公司的大佬,提出一个方案,...

    一、限流操作:

    1. 为什么限流,是防止用户恶意刷新接口,因为部署在外部服务器,并且我们采用websocket的接口实现的,公司没有对硬件升级,导致程序时长崩溃,为了解决这个问题,请教公司的大佬,提出一个方案,限流操作。但是最后找到原因所在,解决了,吞吐量1万6左右,用的测试服务器,进行测试的,我开发的笔记本进行压测,工具是Jmeter,结果我的电脑未响应,卡了,服务器还没有挂。
    2. 限流那些方法
      常见的限流:
      1、Netflix的hystrix
      2、阿里系开源的sentinel
      3、说白了限流,为了处理高并发接口那些方式:队列线程线程池消息队列kafka中间件sentinel:直接拒绝、Warm Up、匀速排队
      技术层面:
      1、判断是否有相同的请求,可以通过自身缓存挡住相同请求
      2、用负载均衡,比如nginx
      3、用缓存数据库,把热点数据get到缓存中,redis,ES
      4、善于使用连接池
      业务层面:
      1、加入交互,排队等待

    二、应用级别限流与限流实现:
    方法一、使用google的guava,令牌桶算法实现:平滑突发限流 ( SmoothBursty) 、平滑预热限流 ( SmoothWarmingUp) 实现

            <!--Java项目广泛依赖 的核心库-->
            <dependency>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
                <version>23.0</version>
            </dependency>
    
    
    package com.citydo.dialogue.controller;
    
    import com.google.common.util.concurrent.RateLimiter;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.Collections;
    
    @RestController
    public class HomeController {
    
        // 这里的1表示每秒允许处理的量为10个
         private RateLimiter limiter = RateLimiter.create(10.0);
        //RateLimiter.create(doublepermitsPerSecond, long warmupPeriod, TimeUnit unit);
        //RateLimiter limiter = RateLimiter.create(5, 1000,  TimeUnit.MILLISECONDS);
        //permitsPerSecond: 表示 每秒新增 的令牌数
        // warmupPeriod: 表示在从 冷启动速率 过渡到 平均速率 的时间间隔
      
    
        @GetMapping("/test/{name}")
        public String Test(@PathVariable("name") String name){
            // 请求RateLimiter, 超过permits会被阻塞
            final double acquire = limiter.acquire();
            System.out.println("--------"+acquire);
            //判断double是否为空或者为0
            if(acquire>=(-1e-6)&&acquire<=(1e-6)){
                return name;
            }else{
                return "操作太频繁";
            }
        }
    }
    

    这个有点类似与QPS流量控制:
    当 QPS 超过某个阈值的时候,则采取措施进行流量控制。
    直接拒绝:
    方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出Exception或者返回值404。这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

    方法二、请求一次redis增加1,key可以是IP+时间或者一个标识+时间,没有就创建,需要设置过期时间
    设置拦截器:

    package com.citydo.dialogue.config;
    
    import com.citydo.dialogue.service.AccessLimitInterceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    /**
     * 拦截器配置
     * @author nick
     */
    
    @Configuration
    public class InterceptorConfig extends WebMvcConfigurationSupport {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //addPathPatterns 添加拦截规则
            registry.addInterceptor(new AccessLimitInterceptor())
                    //添加需要拦截请求的路径
                    .addPathPatterns("/**");
                   //swagger2 放行 .excludePathPatterns("/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
                   //.excludePathPatterns("/*")
                   //去除拦截请求的路径
        }
    
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/");
        }
    }
    
    

    拦截方法

    package com.citydo.dialogue.service;
    
    import com.citydo.dialogue.entity.AccessLimit;
    import com.citydo.dialogue.utils.IpUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.lang.reflect.Method;
    import java.util.concurrent.TimeUnit;
    
    public class AccessLimitInterceptor implements HandlerInterceptor {
    
        //使用RedisTemplate操作redis
    
        @Autowired
        private RedisTemplate<String, Integer> redisTemplate;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Method method = handlerMethod.getMethod();
                if (!method.isAnnotationPresent(AccessLimit.class)) {
                    return true;
                }
                AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
                if (accessLimit == null) {
                    return true;
                }
                int limit = accessLimit.limit();
                int sec = accessLimit.sec();
                String key = IpUtil.getIpAddr(request) + request.getRequestURI();
                //资源唯一标识
                String formatDate=new SimpleDateFormat("yyyyMMddHHmm").format(new Date());
                //String key="request_"+formatDate;
                Integer maxLimit = redisTemplate.opsForValue().get(key);
                if (maxLimit == null) {
                    //set时一定要加过期时间
                    redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);
                } else if (maxLimit < limit) {
                    redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);
                } else {
                    output(response, "请求太频繁!");
                    return false;
                }
            }
            return true;
        }
    
        public void output(HttpServletResponse response, String msg) throws IOException {
            response.setContentType("application/json;charset=UTF-8");
            ServletOutputStream outputStream = null;
            try {
                outputStream = response.getOutputStream();
                outputStream.write(msg.getBytes("UTF-8"));
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                outputStream.flush();
                outputStream.close();
            }
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    }
    

    可以设置成注解,当然也可以直接添加参数

    package com.citydo.dialogue.entity;
    
    import java.lang.annotation.*;
    
    @Inherited
    @Documented
    @Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AccessLimit {
        //标识 指定sec时间段内的访问次数限制
        int limit() default 5;
        //标识 时间段
        int sec() default 5;
    }
    

    redis编写配置

    package com.citydo.dialogue.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    /**
     * 解决redis乱码问题
     * 解决配置问题
     * @author nick
     */
    @Configuration
    public class RedisConfig {
        /**
         * 此方法解决存储乱码
         */
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
            template.setConnectionFactory(redisConnectionFactory);
            template.setKeySerializer(new StringRedisSerializer());
            template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
            template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
            template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
            template.afterPropertiesSet();
            return template;
        }
    }
    

    方法三、分布式限流,分布式限流最关键的是要将限流服务做成原子化,而解决方案可以使用redis+lua或者nginx+lua技术进行实现。

    方法四、可以使用池化技术来限制总资源数:连接池、线程池。比如分配给每个应用的数据库连接是 100,那么本应用最多可以使用 100 个资源,超出了可以 等待 或者 抛异常。

    方法五、限流总并发/连接/请求数,如果你使用过 Tomcat,其 Connector 其中一种配置有如下几个参数:

    maxThreads: 
    Tomcat 能启动用来处理请求的 最大线程数,如果请求处理量一直远远大于最大线程数,可能会僵死。
    maxConnections: 
    瞬时最大连接数,超出的会 排队等待。
    acceptCount: 
    如果 Tomcat 的线程都忙于响应,新来的连接会进入 队列排队,如果 超出排队大小,则 拒绝连接。
    

    方法六、限流某个接口的总并发/请求数,使用 Java 中的 AtomicLong,示意代码:

    try{
        if(atomic.incrementAndGet() > 限流数) {
            //拒绝请求
        } else {
            //处理请求
        }
    } finally {
        atomic.decrementAndGet();
    }
    

    方法七、 限流某个接口的时间窗请求数使用 Guava 的 Cache,示意代码:

    LoadingCache counter = CacheBuilder.newBuilder()
        .expireAfterWrite(2, TimeUnit.SECONDS)
        .build(newCacheLoader() {
            @Override
            public AtomicLong load(Long seconds) throws Exception {
                return newAtomicLong(0);
            }
        });
    longlimit =1000;
    while(true) {
        // 得到当前秒
        long currentSeconds = System.currentTimeMillis() /1000;
        if(counter.get(currentSeconds).incrementAndGet() > limit) {
            System.out.println("限流了: " + currentSeconds);
            continue;
        }
        // 业务处理
    }
    

    不管哪种目的是为了,进行限流操作。
    参考:https://blog.csdn.net/fanrenxiang/article/details/80683378
    参考:https://mp.weixin.qq.com/s/2_oDGJiI1GhaNYnaeL7Qpg
    源码:https://github.com/863473007/springboot_current_limiting

    展开全文
  • 高并发场景限流策略

    千次阅读 2019-11-27 19:57:58
    1、什么是限流和降级 在开发高并发系统时,有很多手段来保护系统:缓存、降级、限流。 当访问量快速增长、服务可能会出现一些问题(响应超时),或者会存在非核心服务影响到核心流程的性能时, 仍然需要保证服务的...

    1、什么是限流和降级

    在开发高并发系统时,有很多手段来保护系统:缓存、降级、限流。
    当访问量快速增长、服务可能会出现一些问题(响应超时),或者会存在非核心服务影响到核心流程的性能时, 仍然需要保证服务的可用性,即便是有损服务。
    所以意味着我们在设计服务的时候,需要一些手段或者关键数据进行自动降级,或者配置人工降级的开关。
    缓存的目的是提升系统访问速度和增大系统处理的容量,可以说是抗高并发流量的银弹;
    降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉某些功能,等高峰后或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如秒杀、抢购;写服务(评论、下单)、频繁的复杂查询,因此需要一种手段来限制这些场景的并发/请求量。

    1.1、降级

    对于高可用服务的设计,有一个很重要的设计,那就是降级。降级一般有几种实现手段,自动降级和人工降级。
    1. 通过配置降级开关,实现对流程的控制
    2. 前置化降级开关, 基于 OpenResty+配置中心实现降级
    3. 业务降级,在大促的时候,我们会有限保证核心业务的流程可用,也就是下单支付。同时,我们也会对核心的支付流程采取一些异步化的方式来提升吞吐量。

    1.2、限流

    限流的目的是防止恶意请求流量、恶意攻击、或者防止流量超过系统峰值限流是对资源访问做控制的一个组件或者功能,那么控制这块主要有两个功能:
    限流策略和熔断策略,对于熔断策略,不同的系统有不同的熔断策略诉求,有得系统希望直接拒绝服务、有的系统希望排队等待、有的系统希望服务降级。限流服务这块有两个核心概念:资源和策略
    资源:被流量控制的对象,比如接口
    策略:限流策略由限流算法和可调节的参数两部份组成

    限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或者告知资源没有了)、排队或等待(秒杀、下单)、降级(返回兜底数据或默认数据或默认数据,如商品详情页库存默认有货)

    1.3、限流和降级

    滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的窗口大小,从而控制发送方的发送速度,从而达到防止发送方发送速度过快而导致自己被淹没的目的。
    简单解释下,发送和接受方都会维护一个数据帧的序列,这个序列被称作窗口。发送方的窗口大小由接受方确定,目的在于控制发送速度,以免接受方的缓存不够大,而导致溢出,同时控制流量也可以避免网络拥塞。下面图中的 4,5,6 号数据帧已经被发送出去,但是未收到关联的 ACK, 7,8,9 帧则是等待发送。可以看出发送端的窗口大小为 6,这是由接受端告知的。此时如果发送端收到 4 号 ACK,则窗口的左边缘向右收缩,窗口的右边缘则向右扩展,此时窗口就向前“滑动了”,即数据帧10 也可以被发送

    动态效果演示地址 https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html

    2、限流算法

    2.1、计数器算法

    计数器算法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开 始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多;如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter,具体算法的示意图如下:

    简易版实现代码:

    public class CounterTest {
        public long timeStamp = getNowTime();
        public int reqCount = 0;
        public final int limit = 100; // 时间窗口内最大请求数
        public final long interval = 1000; // 时间窗口ms
    
        public boolean grant() {
            long now = getNowTime();
            if (now < timeStamp + interval) {
                // 在时间窗口内
                reqCount++;
                // 判断当前时间窗口内是否超过最大请求控制数
                return reqCount <= limit;
            } else {
                timeStamp = now;
                // 超时后重置
                reqCount = 1;
                return true;
            }
        }
    
        public long getNowTime() {
            return System.currentTimeMillis();
        }
    }

    这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题,我们看下图:

    从上图中我们可以看到,假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在 1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。

    聪明的朋友可能已经看出来了,刚才的问题其实是因为我们统计的精度太低。那么如何很好地处理这个问题呢?或者说,如何将临界问题的影响降低呢?我们可以看下面的滑动窗口算法。

    设置一个1s的滑动窗口,窗口有10个格子,每个格子100ms,每100ms移动一次,每次移动都记录当前服务请求的次数。内存中保存最近10次的次数(LinkedList)。格子每次移动的时候判断一次,最后一个格子和第一个格子的次数是否相差100,如果超过,则进行限流。

    当格子划分的越多,那么滑动窗口的滚动越平滑,限流的统计就会越精确。

    2.2、漏桶

    漏桶算法(Leaky Bucket)是网络世界中流量整形(Traffic Shaping)或速率限制(Rate Limiting)时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。

    漏桶作为计量工具时,可用于流量整形和流量控制,漏桶的主要概念如下:

    • 一个固定容量的漏桶,按照常量固定速率流出水滴(流出请求)
    • 如果桶是空的,则不需流出水滴
    • 可以以任意速率流入水滴到漏桶(流入请求)
    • 如果流入水滴超出了桶的容量,则流入的水滴溢出了(新流入的请求被拒绝),则漏桶容量是不变的

    漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。

    如图所示,把请求比作是水,水来了都先放进桶里,并以限定的速度出水,当水来得过猛而出水不够快时就会导致水直接溢出,即拒绝服务。

     

                  

    漏桶可以看做固定容量、固定流出速率的队列,漏桶限制的是请求的流出速率,漏桶中装的是请求。

    因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率.因此,漏桶算法对于存在突发特性的流量来说缺乏效率。

    算法实现:

    public class FunnelRateLimiter { 
      static class Funnel { 
        int capacity; 
        float leakingRate; 
        int leftQuota; 
        long leakingTs; 
     
        public Funnel(int capacity, float leakingRate) { 
          this.capacity = capacity; 
          this.leakingRate = leakingRate; 
          this.leftQuota = capacity; 
          this.leakingTs = System.currentTimeMillis(); 
        } 
     
        void makeSpace() { 
          long nowTs = System.currentTimeMillis(); 
          long deltaTs = nowTs - leakingTs; 
          int deltaQuota = (int) (deltaTs * leakingRate); 
          if (deltaQuota < 0) { // 间隔时间太长,整数数字过大溢出 
            this.leftQuota = capacity; 
            this.leakingTs = nowTs; 
            return; 
          } 
          if (deltaQuota < 1) { // 腾出空间太小,最小单位是 1 
            return; 
          } 
          this.leftQuota += deltaQuota; 
          this.leakingTs = nowTs; 
          if (this.leftQuota > this.capacity) { 
            this.leftQuota = this.capacity; 
          } 
        } 
     
        boolean watering(int quota) { 
          makeSpace(); 
          if (this.leftQuota >= quota) { 
            this.leftQuota -= quota; 
            return true; 
          } 
          return false; 
        } 
      } 
     
      private Map<String, Funnel> funnels = new HashMap<>(); 
     
      public boolean isActionAllowed(String userId, String actionKey, int capacity, float leakingRate) { 
        String key = String.format("%s:%s", userId, actionKey); 
        Funnel funnel = funnels.get(key); 
        if (funnel == null) { 
          funnel = new Funnel(capacity, leakingRate); 
          funnels.put(key, funnel); 
        } 
        return funnel.watering(1); // 需要 1 个 quota 
      } 
    } 

    Funnel 对象的 make_space 方法是漏斗算法的核心,其在每次灌水前都会被调用以触发漏水,给漏斗腾出空间来。能腾出多少空间取决于过去了多久以及流水的速率。Funnel 对象占据的空间大小不再和行为的频率成正比,它的空间占用是一个常量。  

    2.3、令牌桶算法

    令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。令牌桶是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌; 令牌桶算法实际上由三部分组成:两个流和一个桶,分别是令牌流、数据流和令牌桶,大概描述如下:
    1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
    2)根据限流大小,设置按照一定的速率往桶里添加令牌;
    3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
    4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
    5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流;

    令牌桶的一个好处是可以方便的改变速度. 一旦需要提高速率,则按需提高放入桶中的令牌的速率. 一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量。相比漏桶算法另一个优点是能够解决突发流量。

    简单实现代码:

    public class TokenBucketDemo {
        public long timeStamp = getNowTime();
        public int capacity; // 桶的容量
        public int rate; // 令牌放入速度
        public int tokens; // 当前令牌数量
        public boolean grant() {
            long now = getNowTime();
            // 先添加令牌
            //min(桶的容量,当前令牌 + 上次请求获取令牌时间到当前时间内生成的令牌)
            tokens = min(capacity, tokens + (now - timeStamp) * rate);
            timeStamp = now;
            if (tokens < 1) {
                // 若不到1个令牌,则拒绝
                return false;
            }
            else {
                // 还有令牌,领取令牌
                tokens -= 1;
                return true;
            }
        }
    }

    2.4、令牌桶和漏桶的比较

    令牌桶和漏桶的比较
      令牌桶 漏桶
    请求何时拒绝 固定速率往桶中添加令牌,如果桶中令牌不够,则拒绝新请求 流入请求速率任意,常量固定速率流出请求。当流入请求数积累到漏桶容量时,则拒绝新请求
    速率限制 限制平均流入速率,允许一定程度的突发请求(支持一次拿多个令牌) 限制常量流出速率(流出速率是固定值),从而平滑突发流入速率

    3、限流算法的实际应用

    3.1、单机模式限流实现

    在 Guava 中 RateLimiter 的实现有两种: Bursty 和 WarmUp。
    Bursty 是基于 token bucket 的算法实现,比如

    RateLimiter rateLimiter=RateLimiter.create(permitPerSecond); //创建一个 bursty实例
    rateLimiter.acquire(); //获取 1 个 permit,当令牌数量不够时会阻塞直到获取为止

    WarmUp
    1. 基于Leaky bucket 算法实现
    2. QPS 是固定的
    3. 使用于需要预热时间的使用场景

    RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)//创建一个 SmoothWarmingUp 实例;warmupPeriod 是指预热的时间
    RateLimiter rateLimiter=RateLimiter.create(permitsPerSecond,warmupPeriod,timeUnit);
    rateLimiter.acquire();//获取 1 个 permit;可能会被阻塞止到获取到为止

    差异化演示

    public class TokenDemo {
        private int qps;
        private int countOfReq;
        private RateLimiter rateLimiter;
    
        public TokenDemo(int qps, int countOfReq) {
            this.qps = qps;
            this.countOfReq = countOfReq;
        }
    
        public TokenDemo processWithTokenBucket() {
            rateLimiter = RateLimiter.create(qps);
    
            return this;
        }
    
        public TokenDemo processWithLeakyBucket() {
            rateLimiter = RateLimiter.create(qps, 00, TimeUnit.MILLISECONDS);
    
            return this;
        }
    
        private void processRequest() {
            System.out.println("RateLimiter:" + rateLimiter.getClass());
    
            long start = System.currentTimeMillis();
    
            for (int i = 0; i < countOfReq; i++) {
                rateLimiter.acquire();
            }
    
            long end = System.currentTimeMillis() - start;
            System.out.println("处理请求数量:" + countOfReq + "," + "耗时: " + end + "," +
                "qps:" + rateLimiter.getRate() + "," + "实际 qps: " +
                Math.ceil(countOfReq / (end / 1000.00)));
        }
    
        public void doProcess() throws InterruptedException {
            for (int i = 0; i < 20; i = i + 5) {
                TimeUnit.SECONDS.sleep(i);
                processRequest();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            new TokenDemo(50, 100).processWithTokenBucket().doProcess();
            new TokenDemo(50, 100).processWithLeakyBucket().doProcess();
        }
    }

    3.2、集群模式限流实现

    3.2.1、利用redis实现集群计数器算法限流

    用一个 zset 结构记录用户的行为历史,每一个行为都会作为 zset 中的一个 key 保存下来。同一个用户同一种行为用一个 zset 记录。 为节省内存,我们只需要保留时间窗口内的行为记录,同时如果用户是冷用户,滑动时间窗口内的行为是空记录,那么这个 zset 就可以从内存中移除,不再占用空间。 
    通过统计滑动窗口内的行为数量与阈值 max_count 进行比较就可以得出当前的行为是否允许。用代码表示如下: 

    public class SimpleRateLimiter { 
      private Jedis jedis; 
      public SimpleRateLimiter(Jedis jedis) { 
        this.jedis = jedis; 
      } 
      public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) { 
        String key = String.format("hist:%s:%s", userId, actionKey); 
        long nowTs = System.currentTimeMillis(); 
        Pipeline pipe = jedis.pipelined(); 
        pipe.multi(); 
        pipe.zadd(key, nowTs, "" + nowTs); 
        pipe.zremrangeByScore(key, 0, nowTs - period * 1000); 
        Response<Long> count = pipe.zcard(key); 
        pipe.expire(key, period + 1); 
        pipe.exec(); 
        pipe.close(); 
        return count.get() <= maxCount; 
      } 
     
      public static void main(String[] args) { 
        Jedis jedis = new Jedis(); 
        SimpleRateLimiter limiter = new SimpleRateLimiter(jedis); 
        for(int i=0;i<20;i++) { 
          System.out.println(limiter.isActionAllowed("userId", "action", 60, 5)); 
        } 
      } 
    } 

    它的整体思路就是:每一个行为到来时,都维护一次时间窗口。将时间窗口外的记录全部清理掉,只保留窗口内的记录。
    zset 集合中只有 score 值非常重要,value 值没有特别的意义,只需要保证它是唯一的就可以了。 
    因为这几个连续的 Redis 操作都是针对同一个 key 的,使用 pipeline 可以显著提升 Redis 存取效率。

    但这种方案也有缺点,因为它要记录时间窗口内所有的行为记录,如果这个量很大,比如限定 60s 内操作不得超过 100w 次这样的参数,它是不适合做这样的限流的,因为会消耗大量的存储空间。redis的ttl特性完美的满足了这一需求,将时间窗口设置为key的失效时间,然后将key的值每次请求+1即可.伪代码实现思路:

    //1.判断是否存在该key
    if(EXIT(key)){
      // 1.1自增后判断是否大于最大值,并返回结果
      if(INCR(key) > maxPermit){
         return false;
      }
     return true;
    }
     
    //2.不存在key,则设置key初始值为1,失效时间为3秒
    SET(KEY,1);
    EXPIRE(KEY,3);

    3.2.2、Redis-Cell 实现集群漏桶算法

    Redis 4.0 提供了一个限流 Redis 模块,它叫 redis-cell。该模块也使用了漏斗算法,并提供了原子的限流指令。有了这个模块,限流问题就非常简单了。 

    安装方式

    官方提供了安装包和源码编译两种方式,源码编译要安装rust环境,比较复杂,这里介绍安装包方式安装:

    1. 根据操作系统下载安装包;
    2. 将文件解压到redis能访问到的路径下;
    3. 进入 redis-cli,执行命令module load /path/to/libredis_cell.so;

    执行完以上步骤就可以使用其提供的限流功能了。该模块只有 1 条指令 cl.throttle,它的参数和返回值都略显复杂,接下来让我们来看看这个指令具体该如何使用。

    > cl.throttle userId:action 15 30 60 
    1) (integer) 0   # 0 表示允许,1 表示拒绝 
    2) (integer) 15  # 漏斗容量 capacity 
    3) (integer) 14  # 漏斗剩余空间 left_quota 
    4) (integer) -1  # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒) 
    5) (integer) 2   # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒) 

    上面这个指令的意思是允许「用户某行为」的频率为每 60s 最多 30 次(漏水速率),漏斗的初始容量为 15,也就是说一开始可以连续执行 15 次某行为,然后才开始受漏水速率的影响。我们看到这个指令中漏水速率变成了 2 个参数,替代了之前的单个浮点数。用两个参数相除的结果来表达漏水速率相对单个浮点数要更加直观一些。  

    在执行限流指令时,如果被拒绝了,就需要丢弃或重试。cl.throttle 指令考虑的非常周到,连重试时间都帮你算好了,直接取返回结果数组的第四个值进行 sleep 即可,如果不想阻塞线程,也可以异步定时任务来重试。 

    3.2.3、 使用Redisson实现令牌通算法

    基于Redis的分布式限流器(RateLimiter) 可以用来在分布式环境下现在请求方的调用频率。既适用于不同Redisson实例下的多线程限流,也适用于相同Redisson实例下的多线程限流。该算法不保证公平性。

    RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter");
    // 初始化
    // 最大流速 = 每1秒钟产生10个令牌
    rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
    
    CountDownLatch latch = new CountDownLatch(2);
    limiter.acquire(3);
    // ...
    
    Thread t = new Thread(() -> {
        limiter.acquire(2);
        // ...        
    });

     

    展开全文
  • 限流

    千次阅读 2020-07-20 21:40:30
    分布式限流 很多时候我需要有一个全局的限速,例如用户注册时,让用户输入手机验证码,为了防止短信接口不被恶意频繁调用,一般会限制用户每分钟获取验证码频率,例如一分钟不能超过5次。 此时,我们可以通过Redis...

    1、控制单位时间内的请求数

    atomicLong#incrementAndGet()

    分布式限流

    很多时候我需要有一个全局的限速,例如用户注册时,让用户输入手机验证码,为了防止短信接口不被恶意频繁调用,一般会限制用户每分钟获取验证码频率,例如一分钟不能超过5次。

    此时,我们可以通过Redis的来实现,伪代码如下:

    phoneNum = "186xxxxxx";
    key = "verifyCode:limit:"+phoneNum 
    // SET key value EX 60 NX
    isExists = redis.set(key, 1, "EX 60", "NX");
    if( isExists !=null || redis.incr(key) <=5) {
        //通过
    } else {
        //限速
    }
    

    上述,就是通过Redis实现了限速功能,例如一些网站限制一个IP地址不能在一秒钟内访问超过n次也可以采用类似的思路来实现。

    2、并发线程数

    Semaphore


    从上图可以看出上游的A、B服务直接依赖了下游的基础服务C、D和E,对于A,B服务都依赖的基础服务D这种场景,服务A和B其实处于某种竞争关系,当我们考量服务A的并发阈值时,不可忽略的是服务B对A的影响,所以,大多情况并发阈值的设置需要保守一点,如果服务A的并发阈值设置过大,当流量高峰期来临,有可能直接拖垮基础服务D并影响服务B,即雪崩效应来了。从表面上看并发量限流似乎很有用,但也不可否认,它仍然可以造成流量尖刺,即每台服务器上该服务的并发量从0上升到阈值是没有任何“阻力”的,这是因为并发量考虑的只是服务能力边界的问题。

    3、漏斗算法

    均匀的速率

    4、令牌桶算法

    可接受突然大的流量

     

    Sentinel

    对于provider

    根据qps限流

    Service Provider 用于向外界提供服务,处理各个消费者的调用请求。为了保护 Provider 不被激增的流量拖垮影响稳定性,可以给 Provider 配置 QPS 模式的限流,这样当每秒的请求量超过设定的阈值时会自动拒绝多的请求。限流粒度可以是 服务接口 和 服务方法 两种粒度。若希望整个服务接口的 QPS 不超过一定数值,则可以为对应服务接口资源(resourceName 为接口全限定名)配置 QPS 阈值;若希望服务的某个方法的 QPS 不超过一定数值,则可以为对应服务方法资源(resourceName 为接口全限定名:方法签名)配置 QPS 阈值

     

    对于consumer限流

    根据并发数

    Service Consumer 作为客户端去调用远程服务。每一个服务都可能会依赖几个下游服务,若某个服务 A 依赖的下游服务 B 出现了不稳定的情况,服务 A 请求 服务 B 的响应时间变长,从而服务 A 调用服务 B 的线程就会产生堆积,最终可能耗尽服务 A 的线程数。我们通过用并发线程数来控制对下游服务 B 的访问,来保证下游服务不可靠的时候,不会拖垮服务自身。基于这种场景,推荐给 Consumer 配置线程数模式的限流,来保证自身不被不稳定服务所影响。采用基于线程数的限流模式后,我们不需要再显式地去进行线程池隔离,Sentinel 会控制资源的线程数,超出的请求直接拒绝,直到堆积的线程处理完成,可以达到信号量隔离的效果。

    我们看一下这种模式的效果。假设当前服务 A 依赖两个远程服务方法 sayHello(java.lang.String) 和 doAnother()。前者远程调用的响应时间 为 1s-1.5s 之间,后者 RT 非常小(30 ms 左右)。服务 A 端设两个远程方法 thread count 为 5。然后每隔 50 ms 左右向线程池投入两个任务,作为消费者分别远程调用对应方法,持续 10 次。可以看到 sayHello 方法被限流 5 次,因为后面调用的时候前面的远程调用还未返回(RT 高);而 doAnother() 调用则不受影响。线程数目超出时快速失败能够有效地防止自己被慢调用所影响。

     通过降级

    服务降级

    当服务依赖于多个下游服务,而某个下游服务调用非常慢时,会严重影响当前服务的调用。这里我们可以利用 Sentinel 熔断降级的功能,为调用端配置基于平均 RT 的降级规则。这样当调用链路中某个服务调用的平均 RT 升高,在一定的次数内超过配置的 RT 阈值,Sentinel 就会对此调用资源进行降级操作,接下来的调用都会立刻拒绝,直到过了一段设定的时间后才恢复,从而保护服务不被调用端短板所影响。同时可以配合 fallback 功能使用,在被降级的时候提供相应的处理逻辑。


    Fallback

    从 0.1.1 版本开始,Sentinel Dubbo Adapter 还支持配置全局的 fallback 函数,可以在 Dubbo 服务被限流/降级/负载保护的时候进行相应的 fallback 处理。用户只需要实现自定义的 DubboFallback 接口,并通过 DubboFallbackRegistry 注册即可。默认情况会直接将 BlockException 包装后抛出。同时,我们还可以配合 Dubbo 的 fallback 机制 来为降级的服务提供替代的实现。

     

     

     

     

     

     

    快30岁的的我,突然发现自己不再年轻,可能自己消耗的太多,懂得的太晚

     

    我是一名程序媛,很重感情的姑娘,我的童年记忆里我又觉得我是特别的苦,但是很快乐的姑娘

    小时候家里太穷,基本都是看着别人家的孩子在吃好吃的,我只有在那里看着的份,在我记事起,我爸爸腿受伤了,我延迟上学了一年,那年6岁我开始照顾我爸爸,我的童年,我的少年都是很苦的过来的,而我一直在抗争,在与命运抗争

    村里还是重男轻女的,但是我的父母比较的开明,我多次的跟我爸妈谈过话,我说我要上大学,我不想看到到头的生活,那时候我只有9岁,我就知道我要干什么,穷人家的孩子是要受尽各种的苦,才能考上高中,考上大学,我顺利的考上了高中,是年纪里唯一个考上高中的学生,可能我的学校太差,还有我的刻苦,我一直都是年级第一的

    高中的时候生病,考试成绩577分,那年第一年知分填志愿,我与一本无缘,我还是选择离开家乡,去了东北读书

     

    整个过程我觉得我是快乐的,我一直都是成绩很棒,一切我也充满希望,大学毕业,也进入大厂,做起了一个程序媛

    可能我的童年里,太多的与生活做斗争,不知道什么是感情,可能也因为自己的性格里有太多的不安全感,与谈了三年的恋爱告了别分了手,我一直都是在感情里慢半拍的人,分手了半年时间,回了趟大学,想起了点点滴滴,在大学的每一步我都在哭,在想想毕业后自己艰辛,我突然觉得我真的失去了我最爱的人,我是爱他的

     

    然后在回京以后,我路上一直哭,坐地铁自己低着头一直哭的稀里哗啦,我告诉我自己,我真的失去了太多,我没有把握住那个对我那么好,那么单纯付出的人

     

    然后毕业以后在伤痛里沉浸了3个月才像一个正常人一样,突然想开了好多,但是持续了3年,我也没再去真正的去谈恋爱

    自己一直没有走出来,在毕业后的第三年,我觉得我应该走出来了,遇到了公司的同事,他傻乎乎,对人很真诚,对我也足够的忍耐,一起学习,一起玩,最后商量一起来杭州,中间有很多的矛盾,来了杭州才发现,他这个人平时看着足够的忍耐,却脾气爆发的时候像是爆炸,每次都把我炸的自己蹲在地上哭,没有想象的杭州生活的美好,而是各种的矛盾,来了快一年,7个月都在争吵,他也不断的离家出走,最后两者都很受伤,以分手收场

     

    我再看看现在的我自己,老了好多,颓废了好多,觉得自己很惨,怎么就跟他来了这里,没有一个朋友和亲人

    我再看看我的经历,是不是我真的没有爱的能力,28岁的我,被这段感情折磨的已经没有了爱的动力,自己一个人的杭州,没有一点的生机和活力

    在看一直奋斗自己,一直过不了感情这个砍,看到了世界的残酷,人性的恶毒,并不是每个人都善良,对自己好才是王道

     

    我不知道自己何时才能走出来,将近30岁的年级,看着别人都走进了婚姻的殿堂,而我在这里还在舔着伤口

    我一直在想活着的意义,婚姻的本质,是不是自己一直都错了,还是注定要一辈子孤独终老

    此时的自己已经哭的一塌糊涂,毕业的时候班长叮嘱我说,敏敏,你很单纯,心软,在社会上我好怕你被骗,你以后小心点

    我终于知道为什么班长叮嘱我了,可能我真的是很单纯,看事情没那么复杂,可以很快乐的生活着,但是现在的自己,好难受

     

    突然觉得自己长大了,又突然觉得自己没了未来

     

    杭州一个人的城市,不是太容易的,自己也老了吧,不再想着交友,认识新的人,我好想回到去年,甚至大学的时候,我起码是快乐的,更想回到高中的时候,那时候的我不知道感情是这样子有如此大的杀伤力的

     

    生活放眼望去,似乎什么都到头了,甚至恐惧未来的自己

     

    展开全文
  • 高并发之API接口限流

    万次阅读 多人点赞 2018-08-30 10:31:59
    在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流 缓存 缓存的目的是提升系统访问速度和增大系统处理容量 降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再...
  • 接口中的几种限流实现

    千次阅读 2018-12-21 15:11:40
    为什么需要限流 按照服务的调用方,可以分为以下几种类型服务   1、与用户打交道的服务 比如web服务、对外API,这种类型的服务有以下几种可能导致机器被拖垮: 用户增长过快(这是好事) 因为某个热点事件...
  • 浅谈限流(上)

    2019-04-20 06:33:01
    随着应用的访问量越来越高,瞬时流量不可预估,为了保证服务对外的稳定性,限流成为每个应用必备的一道安全防火墙,即使普通的用户也会经常遇到,如微博的限流,抖音的限流,小米抢购的限流…如果没有这道安全防火墙...
  • 高并发处理之接口限流

    万次阅读 2019-01-21 17:15:48
    最近开发的抢购活动上线后发现了两个比较明显的问题,其一:活动一开始,接口访问量剧增;其二:黑名单中增加了一大批黑名单用户...限流的方式也蛮多,本篇只讲几种我自己常用的,并且是后端的限流操作。 漏桶...
  • 我们经常说的限流应该怎么做?

    千次阅读 2019-05-07 14:17:32
    一、我们为什么需要限流 在微服务复杂拓扑的情况下,限流是保障服务弹性和拓扑健壮的重中之重。 想一想,如果业务推出了一个秒杀活动,而你没有任何的限流措施;当你搭建了一个账号平台,而完全没有对十几个业务...
  • sentinel流控设置--关联限流

    万次阅读 2020-06-28 23:48:08
    当A管理的B资源达到阈值,则限流A 相当于支付模块达到阈值,则限流订单模块 请求链接的示例代码 /test002的限流规则 /test001的限流规则 预期效果 由于对/test001的限流控制采用QPS关联,所以直接访问不会被限流...
  • Spring Cloud Zuul微服务网关的API限流

    千次阅读 2018-05-17 15:00:18
    Spring Cloud Zuul微服务网关的API限流更多干货分布式实战(干货)spring cloud 实战(干货)mybatis 实战(干货)spring boot 实战(干货)React 入门实战(干货)构建中小型互联网企业架构(干货)python 学习持续...
  • Redis 实现限流的三种方式

    万次阅读 热门讨论 2020-10-19 11:23:40
    面对越来越多的高并发场景,限流显示的尤为重要。 当然,限流有许多种实现的方式,Redis具有很强大的功能,我用Redis实践了三种的实现方式,可以较为简单的实现其方式。Redis不仅仅是可以做限流,还可以做数据统计...
  • spring cloud gateway 之限流

    万次阅读 热门讨论 2019-09-30 17:50:52
    在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击。 常见的限流方式,比如Hystrix适用线程池隔离,超过线程池的负载,走熔断的逻辑...
  • 常用4种限流算法介绍及比较

    万次阅读 2019-07-16 09:17:36
    1、计数器(固定窗口)算法计数器算法是使用计数器在周期内累加访问次数,当达到设定的限流值时,触发限流策略。下一个周期开始时,进行清零,重新计数。 此算法在单机还是分布式环境下实现都非常简单,使用redis的...
  • Spring Cloud限流详解

    万次阅读 2017-12-01 13:30:32
    Spring Cloud限流详解  Spring Cloud Spring Cloud 2017/12/01 在高并发的应用中,限流往往是一个绕不开的话题。本文详细探讨在Spring Cloud中如何实现限流。 在Zuul 上实现限流是个不错的选择,只...
  • 基于Redis的限流器的实现

    千次阅读 2017-12-27 16:39:55
    1 概述系统中的接口通常都有限流,比如 70次/秒 ,如何保证我们的接口的调用次数在超过第三方接口限流的时候快速失败呢?这时候就需要限流器了。下面是笔者用redis实现限流器的流程图。 2 代码/** * 获取限流权限 ...
  • Spring Cloud Gateway 限流操作

    万次阅读 2019-03-02 19:33:02
    开发高并发系统时有三把利器用来保护系统:缓存、降级和限流,API网关作为所有请求的入口,请求量大,我们可以通过对并发访问的请求进行限速来保护系统的可用性。 常用的限流算法比如有令牌桶算法,漏桶算法,...
  • 使用Guava实现限流

    万次阅读 2018-06-17 13:39:09
    为什么需要限流?在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流限流可以认为服务降级的一种,限流通过限制请求的流量以达到保护系统的目的。一般来说,系统的吞吐量是可以计算出一个阈值的,为了...
  • 【kong系列九】之限流rate-limiting插件

    千次阅读 2017-08-18 16:58:51
    限流插件 rate-limiting
  • SpringCloudGateway限流原理与实践(一) 1 概述 Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project Reactor等技术开发的网关,旨在为微服务架构提供一种简单有效的统一API路由管理方式...
1 2 3 4 5 ... 20
收藏数 572,565
精华内容 229,026
关键字:

限流