参考
- A Guide to Consistent Hashing
- Consistent Hashing Wiki
- How to Design a Scalable Rate Limiting Algorithm
- Rate-limiting strategies and techniques
- Rate Limiting
- Undertanding load balancing in Spring Cloud with Ribbon and example
- Spring Cloud Ribbon设计原理
- Java核心知识点整理
- Hystrix Wiki
- Spring Microservice in Action
负载均衡
- 随机(Random)
- 轮询(RoundRobin)
- 哈希(Hash)
- 加权(Weighted)
- 一致性哈希(ConsistentHash)
一致性Hash
一致性Hash算法(Consistent Hashing Algorithm)是一种分布式算法,常用于负载均衡中。
特性
- 平衡性(Balance):哈希的结果尽可能分布到所有节点中
- 单调性(Monotonicity):如果一些内容已经分派到了响应的节点中,此时又有新的节点加入,Hash结果应能够保证原有分派的内容可以被映射到新的节点中去,而不会被映射到旧的节点中去。比如求余Hash(object % N)无法满足单调性
- 平滑性(Smoothness):服务节点数目平滑改变和负载的平滑改变一致
原理
以三台缓存服务器缓存所有的对象,如下图:
- 构建环形Hash空间:Hash算法将一个value映射为一个32位的key值,即,可以将此空间想象成一个首付相接的圆环,如上图
- 将服务器映射到Hash空间:一般可以使用服务器节点的IP或者服务器名作为Hash输入
- 将对象映射到Hash空间:同服务器映射同样的Hash算法
- 将对象映射到服务器:由对象在Hash环上的位置,顺时针,遇到到第一个服务器即为目标服务器
服务器变动
- 增加一个服务器:假设在C和A之间增加了一个服务器,如下图所示,会影响C和之间的节点,之前映射到A的,现在C和D之间的映射到D,其他节点不变
- 减少一个服务器:假设删除了B节点,则之前A和B之间的节点将全部映射到了C。
虚拟节点
Hash算法并不能保证绝对的平衡性,如果节点减少的话,对象并不能被均匀的映射到各个节点上。为此一致性Hash算法引入了虚拟节点:
- 虚拟节点(virtual node)是实际节点在Hash空间的复制品(replica):一个节点对应了若干个虚拟节点,对应的个数称为复制个数,虚拟节点在Hash空间中以Hash值排列。虚拟节点缓解了映射不均的情况
假设复制个数为2:
引入虚拟节点后,映射关系从【对象 服务器节点】,变为了【对象 虚拟节点 服务器节点】。
负载均衡方式
- 服务端负载均衡:传统方式是将负载均衡放在服务器端。最基础的算法是随机算法。大部分的负载均衡器会综合硬件、软件特性控制。
- 客户端负载均衡:服务器之间是有不同的
- Availability:没有服务器一直可用
- Performance:不同服务器之间性能不同
- Geography:服务器部署在不同的地方。 客服端的负载均衡器通常将请求发送到同一个区的服务器,或者根据响应速度。
应用Ribbon
Ribbon 是netflix 公司开源的基于客户端的负载均衡组件,是Spring Cloud大家庭中非常重要的一个模块。
限流
限流通常是一种保护服务的手段,维持服务可用。防止超出服务频率的调用。在一个可扩展的系统中,在一些层级也要做限流,以减少级联失败(cascading failure)的概率。
大型分布式系统上,客户端和服务器端同时限流,对于最大化吞吐量同时最小化端到端的延迟,至关重要。
作用
防止资源匮乏(Resource Starvation)
在大型系统中,许多无意的基于负载的拒绝服务事件,是由于软件中的错误,或者配置问题,而不是恶意的攻击(比如基于网络的拒绝服务攻击),并非由恶意攻击而引起的资源匮乏,也被称为 friendly-fire denial of service(DOS)。
一般,在受限资源服务前应用一个具有一定预警安全余量的限流。由于负载滞后,余量是必须的,这样限流保护在资源发生严重竞争之前,就可以起到作用。
管理策略和配额
当服务被多个客户端共享使用,就可以在每个客户端做限流,以提供公平而又合理的服务,互不影响。这种限流被称为配额。
流控
在处理大量数据或消息的复杂链路系统中,可以通过流控合并多个流到一个服务,或拆分一个工作到多个工作流。
避免超额
策略
漏桶(Leaky Bucket)
漏桶算法能够限制请求调用的速率
- 漏桶的容量固定,按照固定速率流出水滴(请求)
- 可以以任意速率流入水滴到漏桶
- 如果流入水滴(请求)超过漏桶的容量,则溢出(请求被丢弃)
令牌桶(Token Bucket)
令牌桶算法能够在限制请求调用的平局速率的同时,还允许一定程度的突发调用。
- 令牌桶的容量固定
- 令牌按照固定的速率放入令牌桶中
- 当桶满时,新添加的令牌被丢弃或拒绝
- 令牌桶根据放令牌的速率来控制访问速率
- 请求到达后,首先从令牌桶中获取令牌,获取成功后才可以进行请求处理,处理完成后,将令牌直接删除
对比
| 令牌桶 | 漏桶 | |
|---|---|---|
| 请求何时拒绝 | 令牌桶中令牌不够 | 流入请求数累积超过漏桶容量 |
| 速率限制 | 限制平均速率,允许一定程度的突发请求(支持一次拿多个令牌) | 限制常量流出速率,从而平滑突发流入速率 |
| 丢包问题 | 会 | 会 |
Fixed Window Counter
比如一小时限制3000个请求,但是,有可能3000个请求都同时出现在第一分钟。
- 将timeline切分为固定的时间窗口,并在每个窗口设置计数器
- 每个请求,根据请求时间,被映射到一个窗口
- 如果这个窗口的计数已经达到上限,请求被拒绝
假设每分钟的请求上限是2,则请求过程如下图:
很明显,Fixed Window Counter算法仅保证了每个窗口平均速率,但没有跨窗口。
Sliding Window Log
- 保存了每个请求的时间戳
- 一个请求到来时,首先弹出所有超时的时间戳,然后再将当前请求时间加入Log
- 然后再决定这个请求是否应该被处理,如果Log的大小大于上限,则不处理
Sliding Window
Sliding Window既有Fixed Window的优势,又可以将突发请求,通过滚动的时间窗变的平滑。比如Redis就在过期Keys使用了此技术。
分布式系统中的限流
在含有多个节点的集群中,当需要设置一个全局的限流器时,需要一个全局的存储(比如Redis),这样就存在着以下问题
- Synchronized
- Race Condition:set-then-get
- 中心化计数带来的延迟
使用最终一致性:每个节点周期同步计数。
应用Ribbon
应用Kong
参考 How to Design a Scalable Rate Limiting Algorithm
熔断
断路器Hystrix
线程池模型
- 检查策略