分布式环境 限流解决方案

  • 业务背景介绍

    对于web应用的限流,光看标题,似乎过于抽象,难以理解,那我们还是以具体的某一个应用场景来引入这个话题吧。

    在日常生活中,我们肯定收到过不少不少这样的短信,“双11约吗?,千款….”,“您有幸获得唱读卡,赶快戳链接…”。这种类型的短信是属于推广性质的短信。为什么我要说这个呢?听我慢慢道来。

    一般而言,对于推广营销类短信,它们针对某一群体(譬如注册会员)进行定点推送,有时这个群体的成员量比较大,譬如京东的会员,可以达到千万级别。因此相应的,发送推广短信的量也会增大。然而,要完成这些短信发送,我们是需要调用服务商的接口来完成的。倘若一次发送的量在200万条,而我们的服务商接口每秒能处理的短信发送量有限,只能达到200条每秒。那么这个时候就会产生问题了,我们如何能控制好程序发送短信时的速度昵?于是限流这个功能就得加上了

  • 生产环境背景

    1、服务商接口所能提供的服务上限是400条/s

    2、业务方调用短信发送接口的速度未知,QPS可能达到800/s,1200/s,或者更高

    3、当服务商接口访问频率超过400/s时,超过的量将拒绝服务,多出的信息将会丢失

    4、线上为多节点布置,但调用的是同一个服务商接口

  • 需求分析

    1、鉴于业务方对短信发送接口的调用频率未知,而服务商的接口服务有上限,为保证服务的可用性,业务层需要对接口调用方的流量进行限制—–接口限流

  • 需求设计

    方案一、在提供给业务方的Controller层进行控制。

    1、使用guava提供工具库里的RateLimiter类(内部采用令牌捅算法实现)进行限流

<!--核心代码片段-->
private RateLimiter rateLimiter = RateLimiter.create(400);//400表示每秒允许处理的量是400
 if(rateLimiter.tryAcquire()) {
   //短信发送逻辑可以在此处

 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2、使用Java自带delayqueue的延迟队列实现(编码过程相对麻烦,此处省略代码)

3、使用redis实现,存储两个key,一个用于计时,一个用于计数。请求每调用一次,计数器增加1,若在计时器时间内计数器未超过阈值,则可以处理任务

 if(!cacheDao.hasKey(API_WEB_TIME_KEY)) {            cacheDao.putToValue(API_WEB_TIME_KEY,0,(long)1, TimeUnit.SECONDS);
}       if(cacheDao.hasKey(API_WEB_TIME_KEY)&&cacheDao.incrBy(API_WEB_COUNTER_KEY,(long)1) > (long)400) {
    LOGGER.info("调用频率过快");
}
//短信发送逻辑
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

方案二、在短信发送至服务商时做限流处理

方案三、同时使用方案一和方案二

  • 可行性分析

    最快捷且有效的方式是使用RateLimiter实现,但是这很容易踩到一个坑,单节点模式下,使用RateLimiter进行限流一点问题都没有。但是…线上是分布式系统,布署了多个节点,而且多个节点最终调用的是同一个短信服务商接口。虽然我们对单个节点能做到将QPS限制在400/s,但是多节点条件下,如果每个节点均是400/s,那么到服务商那边的总请求就是节点数x400/s,于是限流效果失效。使用该方案对单节点的阈值控制是难以适应分布式环境的,至少目前我还没想到更为合适的方式。

    对于第二种,使用delayqueue方式。其实主要存在两个问题,1:短信系统本身就用了一层消息队列,有用kafka,或者rabitmq,如果再加一层延迟队列,从设计上来说是不太合适的。2:实现delayqueue的过程相对较麻烦,耗时可能比较长,而且达不到精准限流的效果

    对于第三种,使用redis进行限流,其很好地解决了分布式环境下多实例所导致的并发问题。因为使用redis设置的计时器和计数器均是全局唯一的,不管多少个节点,它们使用的都是同样的计时器和计数器,因此可以做到非常精准的流控。同时,这种方案编码并不复杂,可能需要的代码不超过10行。

  • 实施方案

    根据可行性分析可知,整个系统采取redis限流处理是成本最低且最高效的。

    具体实现

    1、在Controller层设置两个全局key,一个用于计数,另一个用于计时

private static final String API_WEB_TIME_KEY = "time_key";

    private static final String API_WEB_COUNTER_KEY = "counter_key";
  • 1
  • 2
  • 3

2、对时间key的存在与否进行判断,并对计数器是否超过阈值进行判断

if(!cacheDao.hasKey(API_WEB_TIME_KEY)) {

            cacheDao.putToValue(API_WEB_TIME_KEY,0,(long)1, TimeUnit.SECONDS);
            cacheDao.putToValue(API_WEB_COUNTER_KEY,0,(long)2, TimeUnit.SECONDS);//时间到就重新初始化为

        }

        if(cacheDao.hasKey(API_WEB_TIME_KEY)&&cacheDao.incrBy(API_WEB_COUNTER_KEY,(long)1) > (long)400) {

            LOGGER.info("调用频率过快");

        }
         //短信发送逻辑
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

实施结果

可以达到非常精准的流控,截图会在后续的过程中贴出来。欢迎有疑问的小伙伴们在评论区提出问题,我看到后尽量抽时间回答的

版权声明:本文为博主原创文章,未经博主允许不得转载。 http://blog.csdn.net/Justnow_/article/details/53055299

原文地址:https://www.cnblogs.com/xifenglou/p/8519702.html

时间: 2024-10-08 11:27:35

分布式环境 限流解决方案的相关文章

分布式环境下限流方案的实现redis RateLimiter Guava,Token Bucket, Leaky Bucket

对于web应用的限流,光看标题,似乎过于抽象,难以理解,那我们还是以具体的某一个应用场景来引入这个话题吧. 在日常生活中,我们肯定收到过不少不少这样的短信,"双11约吗?,千款-.","您有幸获得唱读卡,赶快戳链接-".这种类型的短信是属于推广性质的短信.为什么我要说这个呢?听我慢慢道来. 一般而言,对于推广营销类短信,它们针对某一群体(譬如注册会员)进行定点推送,有时这个群体的成员量比较大,譬如京东的会员,可以达到千万级别.因此相应的,发送推广短信的量也会增大.然

基于redis+lua实现高并发场景下的秒杀限流解决方案

转自:https://blog.csdn.net/zzaric/article/details/80641786 应用场景如下: 公司内有多个业务系统,由于业务系统内有向用户发送消息的服务,所以通过统一消息系统对外暴露微服务接口供外部业务系统调用,所有公司内业务系统的消息(短信,APP,微信)推送都由统一消息系统去推送,短信推送需要走外部短信通道商去发送短信,APP和微信走内部系统的push服务器,但是不管是短信通道商还是内部push服务器都会有每秒上限的控制.在这假设n/s条. 以下是统一消息

优雅解决分布式限流

SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程 在前面的两篇文章中,介绍了一些限流的类型和策略,本篇从 Spring Boot.Redis 应用层面来实现分布式的限流…. 分布式限流 单机版中我们了解到 AtomicInteger.RateLimiter.Semaphore 这几种解决方案,但它们也仅仅是单机

巧用SpringBoot优雅解决分布式限流

SpringBoot?是为了简化?Spring?应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程 本篇从?Spring Boot.Redis?应用层面来实现分布式的限流-. 分布式限流 单机版中我们了解到 AtomicInteger.RateLimiter.Semaphore 这几种解决方案,但它们也仅仅是单机的解决手段,在集群环境下就透心凉了,后面又讲述了

Spring Cloud限流思路及解决方案

转自: http://blog.csdn.net/zl1zl2zl3/article/details/78683855 在高并发的应用中,限流往往是一个绕不开的话题.本文详细探讨在Spring Cloud中如何实现限流. 在 Zuul 上实现限流是个不错的选择,只需要编写一个过滤器就可以了,关键在于如何实现限流的算法.常见的限流算法有漏桶算法以及令牌桶算法.这个可参考 https://www.cnblogs.com/LBSer/p/4083131.html ,写得通俗易懂,你值得拥有,我就不拽文

限流实现与解决方案

https://blog.csdn.net/qq_32447301/article/details/86659474 一.限流操作: 为什么限流,是防止用户恶意刷新接口,因为部署在外部服务器,并且我们采用websocket的接口实现的,公司没有对硬件升级,导致程序时长崩溃,为了解决这个问题,请教公司的大佬,提出一个方案,限流操作.但是最后找到原因所在,解决了,吞吐量1万6左右,用的测试服务器,进行测试的,我开发的笔记本进行压测,工具是Jmeter,结果我的电脑未响应,卡了,服务器还没有挂.限流那

用nginx实现分布式限流(防DDOS攻击)

1.前言 一般对外暴露的系统,在促销或者黑客攻击时会涌来大量的请求,为了保护系统不被瞬间到来的高并发流量给打垮, 就需要限流 . 本文主要阐述如何用nginx 来实现限流. 听说 Hystrix 也可以, 各位有兴趣可以去研究哈 . 2. 首先部署一个对外暴露接口的程序 我这里部署的是一个spring boot 项目 里面暴露了如下接口, 很简单 package com.anuo.app.controller; import org.slf4j.Logger; import org.slf4j.

从构建分布式秒杀系统聊聊限流的多种实现

前言 俗话说的好,冰冻三尺非一日之寒,滴水穿石非一日之功,罗马也不是一天就建成的.两周前秒杀案例初步成型,分享到了中国最大的同×××友网站-码云.同时也收到了不少小伙伴的建议和投诉.我从不认为分布式.集群.秒杀这些就应该是大厂的专利,在互联网的今天无论什么时候都要时刻武装自己,只有这样,也许你的春天就在明天. 在开发秒杀系统案例的过程中,前面主要分享了队列.缓存.锁和分布式锁以及静态化等等.缓存的目的是为了提升系统访问速度和增强系统的处理能力:分布式锁解决了集群下数据的安全一致性问题:静态化无疑

从构建分布式秒杀系统聊聊限流特技

前言 俗话说的好,冰冻三尺非一日之寒,滴水穿石非一日之功,罗马也不是一天就建成的.两周前秒杀案例初步成型,分享到了中国最大的同性交友网站-码云.同时也收到了不少小伙伴的建议和投诉.我从不认为分布式.集群.秒杀这些就应该是大厂的专利,在互联网的今天无论什么时候都要时刻武装自己,只有这样,也许你的春天就在明天. 在开发秒杀系统案例的过程中,前面主要分享了队列.缓存.锁和分布式锁以及静态化等等.缓存的目的是为了提升系统访问速度和增强系统的处理能力:分布式锁解决了集群下数据的安全一致性问题:静态化无疑是