网关限流分布式解决方案+单机网关限流

  • 背景介绍
  • 实现的功能
  • 技术选型
  • 限流算法
  • 漏桶算法
  • 令牌桶算法
  • 令牌桶和漏桶对比
  • 两种算法的区别
  • 令牌桶的实现
  • Lua处理过程
  • 其他解决方案
  • 单机网关限流


背景介绍

微服务网关模块将实现网关集群部署,并且登录、鉴权和配额管理都会依赖另一个权限系统,为实现分布式下的并发控制和配额管理,提出解决方案。

实现的功能

在分布式下网关集群中,限制每个用户访问每个方法并发访问量和每日访问总量,也就是限流,准确来说是并发控制和配额管理

技术选型

Redis + Lua脚本实现令牌桶限流 ActiveMQ实现接收权限系统限额调整

限流算法

常见的限流算法有:令牌桶、漏桶。计数器也可以进行粗暴限流实现。

漏桶算法

漏桶作为计量工具(The Leaky Bucket Algorithm as a Meter)时,可以用于流量整形(Traffic Shaping)和流量控制(TrafficPolicing),漏桶算法的描述如下:

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

java分布式限流实现 分布式限流方案_java分布式限流实现

令牌桶算法

令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌。令牌桶算法的描述如下:

  • 假设限制2r/s,则按照1秒的固定速率往桶中添加令牌;
  • 桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃或拒绝;
  • 当一个n个请求到达,将从桶中删除n个令牌,接着请求被通过;
  • 如果桶中的令牌不足n个,则不会删除令牌,且该请求将被限流(要么丢弃,要么缓冲区等待)。

java分布式限流实现 分布式限流方案_lua_02

令牌桶和漏桶对比

  • 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;
  • 漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;
  • 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌),并允许一定程度突发流量;
  • 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),从而平滑突发流入速率;
  • 令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率;
  • 两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。

两种算法的区别

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

令牌桶的实现

分布式系统解决方案是用Redis+Lua脚本实现使用redis进行限流,redis的单线程操作特性来执行lua脚本,通过lua脚本来保证原子性(将脚本作为一个整体执行,中间不会插入其他命令,无需使用事务)而且可以减少网络开销(多个请求通过脚本一次发送,减少网络延迟),这样的方案很好地解决了分布式环境下多实例所导致的并发问题。因为使用redis设置的计时器和计数器均是全局唯一的,不管多少个节点,它们使用的都是同样的计时器和计数器,因此可以做到非常精准的流控令牌桶限流。

Redis中存储类型设置为Hash存储,存储令牌桶对象的信息,在redis中存储示例如图:

java分布式限流实现 分布式限流方案_java分布式限流实现_03

  • Hash对象key为用户id+方法的组合(上图用户id为1,方法为maptileInfo)
  • last_mill_second:上一次操作时间(毫秒值)
  • stored_permits:目前桶中可用的令牌数
  • max_permits:桶容量
  • oneSecondNum:每秒生成令牌的个数
  • stored_everyday_limit:本日当前还可以消耗的限额数(本日当前余额)
  • everyday_limit:每日限额

Lua处理过程

1.判断当前时间和上次操作时间间隔,如果不是同一天的话本日当前限额数=本日限额
2.计算到当前该放多少令牌(当前时间-上次操作时间间隔)* 每秒生成令牌数
3.计算当前剩余多少令牌 = min(桶容量, 目前桶中令牌数+当前该放的令牌数) ,最大值为桶的容量
4.如果当前剩余多少令牌 >=1,并且本日当前限额数>=1,则消耗令牌放行

java分布式限流实现 分布式限流方案_java分布式限流实现_04

其他解决方案

思考:网关限流redis+lua的分布式解决方案,如果请求量特别大,保证请求的顺序和时间,包括原子性等问题,这些还需要进一步压测。还有令牌桶容量,时间间隔,每秒生成速率怎么设置合适,希望有能力,有兴趣的大神交流、指教。

单机网关限流

单机网关限流比较简单,不需要考虑集群问题,令牌桶实现方法:
单机令牌桶