高并发场景下的流控管理

任何应用都有一个设计指标,当应用的压力超过了他设计所能承载的能力时,就好比一座只允许行人通过的独木桥,是无法承载一辆坦克的重量的,这个时候,为了让机器能够继续运行,在不宕机的情况下尽其所能的对一部分用户提供服务,保证整个流程能够继续走下去,这个时候,就必须对应用进行流控,丢弃一部分用户的请无法避免。

流控可以从多个维度来进行,比如针对QPS并发线程数黑白名单加权分级等等,最典型最直接的便是针对QPS并发线程数的流控。当然,要进行流控,首先等有一个流控的阀值,这个阀值不是说拍拍脑袋就能够想出来,不同类型的应用,所面临的情况不一样,也没有一个统一的衡量标准,必须经过多轮的压力测试,才能够得出一个比较靠谱的数值。

一、简单的流控

1、使用Semphore进行并发流控

模拟代码如下所示:

  Semaphore semphore = new Semaphore(10);
  if(semphore.getQueueLength() > 10){
   //等待队列阀值为10时
    return;
  }
  try {
   semphore.acquire();  

   //干活  

  } catch (InterruptedException e) {
       e.printStackTrace();
  }finally{
     semphore.release();//释放
  }  

也可以参见:http://ifeve.com/concurrency-practice-1/

2、使用乐观锁加上下文切换进行流控

public void enter(Object obj){  

    boolean isUpdate = false;
    int countValue = count.get();
    if(countValue > 0){
        isUpdate = count.compareAndSet(countValue, countValue -1);
        if(isUpdate)return;
    }  

    concurQueue.add(obj);  

    try {
        obj.wait();
    } catch (InterruptedException e) {
        logger.error("flowcontrol thread was interrupted .......",e);
    }
    return ;
 }  

public void release(){  

    synchronized(count){
        if(count.get() < VALVE){
            count.set(count.get() + 1);
        }
    }  

    Object obj = concurQueue.remove();
    if(obj != null){
        synchronized (obj) {
            obj.notify();
        }
    }
    System.out.println("notify ...............");
    return ;
}  

具体采用信号量还是使用上下文切换形式,需要根据临界代码段执行的时间而定

当请求进来时,调用配置的concurrentlock的enter方法,判断是否达到阀值,如果没有达到阀值,则进入,进行处理, 处理完后计数器加1,如果已经达到阀值则放入等待队列,因为等待队列是消耗内存的,因此等待队列也必须有阀值,如果队列超过阀值,请求直接丢弃
二、漏斗算法和桶令牌算法

利用现存的算法,比如:漏斗算法和桶令牌算法进行流量的控制。

关于漏斗算法和桶令牌算法可以参考:http://www.inter12.org/archives/962

https://blog.jamespan.me/2015/10/19/traffic-shaping-with-token-bucket/

1、漏桶算法(Leaky bucket)

漏桶算法强制一个常量的输出速率而不管输入数据流的突发性,当输入空闲时,该算法不执行任何动作.就像用一个底部开了个洞的漏桶接水一样,水进入到漏桶里,桶里的水通过下面的孔以固定的速率流出,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率.如下图所示:

2、令牌桶(Token bucket)

令牌桶算法的基本过程如下:

  1. 每秒会有 r 个令牌放入桶中,或者说,每过 1/r 秒桶中增加一个令牌
  2. 桶中最多存放 b 个令牌,如果桶满了,新放入的令牌会被丢弃
  3. 当一个 n 字节的数据包到达时,消耗 n 个令牌,然后发送该数据包
  4. 如果桶中可用令牌小于 n,则该数据包将被缓存或丢弃

漏桶和令牌桶比较

“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输数据外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的上限,因此它适合于具有突发特性的流量。

时间: 2024-10-11 07:37:33

高并发场景下的流控管理的相关文章

高并发场景下的限流策略

高并发场景下的限流策略: 在开发高并发系统时,有很多手段来保护系统:缓存.降级.限流. 当访问量快速增长.服务可能会出现一些问题(响应超时),或者会存在非核心服务影响到核心流程的性能时, 仍然需要保证服务的可用性,即便是有损服务.所以意味着我们在设计服务的时候,需要一些手段或者关键数据进行自动降级,或者配置人工降级的开关. 缓存的目的是提升系统访问速度和增大系统处理的容量,可以说是抗高并发流量的银弹:降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉某些功能,等高峰或者问题解决后再打开:

缓存在高并发场景下的常见问题

缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象.这就比较依赖缓存的过期和更新策略.一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存. 缓存并发问题 缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程.但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 “雪崩”现象.此外,当某个缓存key在被更新时,同时也可能被大量请求在获取,

高并发场景下的缓存有哪些常见的问题?

一.缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象. 这就比较依赖缓存的过期和更新策略.一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存. 二.缓存并发问题 缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程.但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 "雪崩"现象. 此外,当某个缓存key在被更新时,同时也

高并发场景下使用缓存需要注意那些问题?

一.缓存一致性问题 当数据时效性要求很高时,需要保证缓存中的数据与数据库中的保持一致,而且需要保证缓存节点和副本中的数据也保持一致,不能出现差异现象.这就比较依赖缓存的过期和更新策略.一般会在数据发生更改的时,主动更新缓存中的数据或者移除对应的缓存. 二.缓存并发问题 缓存过期后将尝试从后端数据库获取数据,这是一个看似合理的流程.但是,在高并发场景下,有可能多个请求并发的去从数据库获取数据,对后端数据库造成极大的冲击,甚至导致 "雪崩"现象.此外,当某个缓存key在被更新时,同时也可能

【转】记录PHP、MySQL在高并发场景下产生的一次事故

看了一篇网友日志,感觉工作中值得借鉴,原文如下: 事故描述 在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达42亿,但是当时一直没有到证据表示这是,确实存在,并且直觉告诉我们,这是不可能的,就一直没有在意,直到后来真的发现了一个用户确实是42亿,当时我们整个公司都震惊了,如果有大量用户是这样的情况,公司要亏损几十万,我们的老大告诉我们,肯定是什么地方数据溢出的,最后我们一帮人,疯了似的查代码,发现…… 如果按照正常的程序逻辑走下去,代码是完全没问题,但是我发

高并发场景下秒杀项目静态锁的使用疑问

题:高并发场景下秒杀项目静态锁的使用疑问场景:我们有一个秒杀平台,可以提供所有接入公司创建的秒杀活动,简单描述如下:1.秒杀10袋洗衣粉,开始时间12:00(项目ID:A001)2.秒杀iPhone5,开始时间12:00(项目ID:A002)3.秒杀水杯,开始时间12:00(项目ID:A003)... ...(项目ID:A004-A009)10.秒杀ThinkPad,开始时间12:00(项目ID:A010) 例如上面,同时有十个秒杀,都是12:00整开始,每个秒杀之间没有任何关系. 按照我之前的

高并发场景下System.currentTimeMillis()的性能问题的优化

前言 System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我也不知道,不过听说在100倍左右),然而该方法又是一个常用方法,有时不得不使用,比如生成wokerId.打印日志什么的,在高并发情形下肯定存在性能问题的,但怎么做才好呢? System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道.那什么快?内存!如果该方法从内存直接取数,那不就美滋滋了. 代码实现 package com.nyvi.support.uti

高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器

package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; /** * 高并发场景下System.currentTimeMillis()的性能问题的优化 * <p><p> * System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100

高并发场景下请求合并的实践

前言 项目中一般会请求第三方的接口,也会对外提供接口,可能是RPC,也可能是HTTP等方式.在对外提供接口时,有必要提供相应的批量接口,好的批量实现能够提升性能. 高并发场景中,调用批量接口相比调用非批量接口有更大的性能优势.但有时候,请求更多的是单个接口,不能够直接调用批量接口,如果这个接口是高频接口,对其做请求合并就很有必要了.比如电影网站的获取电影详情接口,APP的一次请求是单个接口调用,用户量少的时候请求也不多,完全没问题:但同一时刻往往有大量用户访问电影详情,是个高并发的高频接口,如果