使用shuffle sharding增加容错性
使用shuffle sharding增加容错性
最近在看kubernetes的API Priority and Fairness,它使用shuffle sharding来为请求选择处理队列,以此防止高吞吐量流挤占低吞吐量流,进而造成请求延迟的问题。
介绍
首先看下什么是shuffle sharding,下面内容来自aws的Workload isolation using shuffle-sharding。
首先来看如何使用一般分片方式来让系统具备可扩展性和弹性。
假设有一个8 workers节点的水平可扩展的系统或服务,下图红线表示达到这些节点的请求,worker可以是服务,队列或数据库等。
如果没有任何分片,则要求每个worker能够处理所有请求。这种方式高效且具备一定的冗余性。如果一个worker出现故障,则可以将它的任务分配到剩余的7个worker上。此时可能需要增加一定的系统容量。但如果突然出现大量请求,如DDoS攻击,可能会导致级联故障。下面两张图展示了故障是如何升级的。
首先会影响第一台worker,随后会级联到其他workers上,最终导致整个服务不可用。
为了防止故障转移,通常可以使用分片方式,如将workers分为4个分片,以效率换取影响度。下面两张图展示了如何使用分片来限制DDoS攻击。
本例中,每个分片包含2个workers,并按照资源(如域名)进行切片。此时的系统仍然具有冗余性,但由于每个分片只有2个workers,因此可能需要增加容量来避免故障。
通过这种方式降低了故障影响范围。这里有4个分片,如果一个分片故障,则只会影响该分片上的服务,其他分片则不受影响。影响范围为25%。使用shuffle sharding可以达到更好的效果。
shuffle sharding用到了虚拟分片(shuffle shard)的概念,这里将不会直接对workers进行分片,而是按照"用户"进行分片,目的是尽量将用户打散分布到不同的worker上。
下图展示的shuffle sharding布局中包含8个workers和8个客户,并给每个客户分配了2个workers。以彩虹和玫瑰表示的客户为例。
这里,我们给彩虹客户分配了第1个和第4个worker,这两个workers构成了该客户的shuffle shard,其他客户将使用不同的虚拟分片(含2个workers),如玫瑰客户分配了第1个和最后一个worker。
如果彩虹用户分配的worker 1和worker 4出现了问题(如恶意请求或请求泛红等),则此问题只会影响本虚拟分片,但不会影响到其他shuffle shard。事实上,最多只会有另外一个shuffle shard会受到影响(即另外一个服务都部署到了worker 1和worker 4)。如果请求方具有容错性,则可以继续使用剩余分片继续提供服务。
换句话说,当彩虹客户所在的节点因为出现问题或受到攻击而无法提供服务时,不会影响到其他节点。对于客户而言,虽然玫瑰客户和向日葵客户都和彩虹客户共享了worker,但并没有导致其服务中断,玫瑰客户仍然可以继续使用workers 8提供服务,而向日葵客户可以继续使用worker 6提供服务。
当出现上述问题时,虽然失去了四分之一的worker节点,但使用shuffle sharding可以大大降低影响范围。上述场景下,一共有28种两两worker的组合方式,即28种shuffle shards。当有上百甚至更多的客户时,我们可以给每个客户分配一个shuffle shards,以此可以将影响范围缩小到1/28,效果是一般分片方式的7倍。
kubernetes中的shuffle sharding
使用shuffle sharding为流分片队列
kubernetes的流控功能中使用了shuffle sharding,其代码实现如下:
func NewDealer(deckSize, handSize int) (*Dealer, error) {
if deckSize <= 0 || handSize <= 0 {
return nil, fmt.Errorf("deckSize %d or handSize %d is not positive", deckSize, handSize)
}
if handSize > deckSize {
return nil, fmt.Errorf("handSize %d is greater than deckSize %d", handSize, deckSize)
}
if deckSize > 1<<26 {
return nil, fmt.Errorf("deckSize %d is impractically large", deckSize)
}
if RequiredEntropyBits(deckSize, handSize) > MaxHashBits {
return nil, fmt.Errorf("required entropy bits of deckSize %d and handSize %d is greater than %d", deckSize, handSize, MaxHashBits)
}
return &Dealer{
deckSize: deckSize,
handSize: handSize,
}, nil
}
func (d *Dealer) Deal(hashValue uint64, pick func(int)) {
// 15 is the largest possible value of handSize
var remainders [15]int
//这个for循环用于生成[0,deckSize)范围内的随机数。
for i := 0; i < d.handSize; i++ {
hashValueNext := hashValue / uint64(d.deckSize-i)
remainders[i] = int(hashValue - uint64(d.deckSize-i)*hashValueNext)
hashValue = hashValueNext
}
for i := 0; i < d.handSize; i++ {
card := remainders[i]
for j := i; j > 0; j-- {
if card >= remainders[j-1] {
card++
}
}
pick(card)
}
}
func (d *Dealer) DealIntoHand(hashValue uint64, hand []int) []int {
h := hand[:0]
d.Deal(hashValue, func(card int) { h = append(h, card) })
return h
}
首先使用
func NewDealer(deckSize, handSize int)
初始化一个实例,以kubernetes的APF功能为例,deckSize
为队列数,handSize
表示为一条流分配的队列数量使用
func (d *Dealer) DealIntoHand(hashValue uint64, hand []int)
可以返回为流选择的队列ID,hashValue
可以看做是流的唯一标识,hand
为存放结果的数组。hashValue
的计算方式如下,fsName为flowschemas
的名称,fDistinguisher
可以是用户名或namespace名称:func hashFlowID(fsName, fDistinguisher string) uint64 {
hash := sha256.New()
var sep = [1]byte{0}
hash.Write([]byte(fsName))
hash.Write(sep[:])
hash.Write([]byte(fDistinguisher))
var sum [32]byte
hash.Sum(sum[:0])
return binary.LittleEndian.Uint64(sum[:8])
}
用法如下:
var backHand [8]int
deal, _ := NewDealer(128, 9)
fmt.Println(deal.DealIntoHand(8238791057607451177, backHand[:]))
//输出:[41 119 0 49 67]
为请求分片队列
上面为流分配了队列,实现了流之间的队列均衡。此时可能为单条流分配了多个队列,下一步就是将单条流的请求均衡到分配到的各个队列中。核心代码如下:
func (qs *queueSet) shuffleShardLocked(hashValue uint64, descr1, descr2 interface{}) int {
var backHand [8]int
// Deal into a data structure, so that the order of visit below is not necessarily the order of the deal.
// This removes bias in the case of flows with overlapping hands.
//获取本条流的队列列表
hand := qs.dealer.DealIntoHand(hashValue, backHand[:])
handSize := len(hand)
//qs.enqueues表示队列中的请求总数,这里第一次哈希取模算出队列的起始偏移量
offset := qs.enqueues % handSize
qs.enqueues++
bestQueueIdx := -1
minQueueSeatSeconds := fqrequest.MaxSeatSeconds
//这里用到了上面的偏移量,并考虑到了队列处理延迟,找到延迟最小的那个队列作为目标队列
for i := 0; i < handSize; i++ {
queueIdx := hand[(offset+i)%handSize]
queue := qs.queues[queueIdx]
queueSum := queue.requests.QueueSum()
// this is the total amount of work in seat-seconds for requests
// waiting in this queue, we will select the queue with the minimum.
thisQueueSeatSeconds := queueSum.TotalWorkSum
klog.V(7).Infof("QS(%s): For request %#+v %#+v considering queue %d with sum: %#v and %d seats in use, nextDispatchR=%v", qs.qCfg.Name, descr1, descr2, queueIdx, queueSum, queue.seatsInUse, queue.nextDispatchR)
if thisQueueSeatSeconds < minQueueSeatSeconds {
minQueueSeatSeconds = thisQueueSeatSeconds
bestQueueIdx = queueIdx
}
}
...
return bestQueueIdx
}
使用shuffle sharding增加容错性的更多相关文章
- Hadoop笔记HDFS(1)
环境:Hadoop2.7.3 1.Benchmarking HDFS 1.1测试集群的写入 运行基准测试是检测HDFS集群是否正确安装以及表现是否符合预期的好方法.DFSIO是Hadoop自带的一个基 ...
- SharePoint咨询师之路:设计之前的那些事四:负载均衡 - web服务器
提示:本系列只是一个学习笔记系列,大部分内容都可以从微软官方网站找到,本人只是按照自己的学习路径来学习和呈现这些知识.有些内容是自己的经验和积累,如果有不当之处,请指正. 容量管理 规模 体系结构 ...
- 【转载】Apache Spark Jobs 性能调优(一)
当你开始编写 Apache Spark 代码或者浏览公开的 API 的时候,你会遇到各种各样术语,比如 transformation,action,RDD 等等. 了解到这些是编写 Spark 代码的 ...
- Apache Spark Jobs 性能调优
当你开始编写 Apache Spark 代码或者浏览公开的 API 的时候,你会遇到各种各样术语,比如transformation,action,RDD(resilient distributed d ...
- 消息队列中间件(三)Kafka 入门指南
Kafka 来源 Kafka的前身是由LinkedIn开源的一款产品,2011年初开始开源,加入了 Apache 基金会,2012年从 Apache Incubator 毕业变成了 Apache 顶级 ...
- Kafka 基本概念学习笔记
一. 什么是Kafka 面向数据流的生产,转换,存储,消费的整体流处理平台 二.Kafka三大特性 1.发布和订阅数据的流,类似于消息队列,消息系统 2..数据流存储平台 3.当数据产生的时候,对数据 ...
- Kafka流处理平台
1. Kafka简介 Kafka是最初由Linkedin公司开发,是一个分布式.支持分区的(partition).多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性 ...
- Android注入完全剖析
0 前沿 本文主要分析了一份实现Android注入的代码的技术细节,但是并不涉及ptrace相关的知识,所以读者如果不了解ptrace的话,最好先学习下ptrace原理再来阅读本文.首先,感谢源代码的 ...
- USB 3.0规范中译本 第7章 链路层
本文为CoryXie原创译文,转载及有任何问题请联系cory.xie#gmail.com. 链路层具有维持链路连接性的责任,从而确保在两个链路伙伴之间的成功数据传输.基于包(packets)和链路命令 ...
- ~~番外:说说Python 面向对象编程~~
进击のpython Python 是支持面向对象的 很多情况下使用面向对象编程会使得代码更加容易扩展,并且可维护性更高 但是如果你写的多了或者某一对象非常复杂了,其中的一些写法会相当相当繁琐 而且我们 ...
随机推荐
- SignalR+Hangfire 实现后台任务队列和实时通讯
SignalR+Hangfire 实现后台任务队列和实时通讯 1.简介: SignalR是一个.NET的开源框架,SignalR可使用Web Socket, Server Sent Events 和 ...
- Windows操作系统中常用的Dos命令
打开CMD的方式: Win+R 输入cmd打开控制台(推荐使用) 在任意文件夹下,按住shift键,点击鼠标右键,选择在此处打开命令行窗口 在资源管理器的地址栏路径前面加上cmd+空格: 常用的Dos ...
- hugp-MemE关键美化
配置front matter 使用vscode snippet快捷生成front matter 参考博客:vs-code-workflows-for-hugo. markdown-snippets-n ...
- SQL Server 日志传输还原作业执行缓慢
目录 情景 故障定位 VLF 对 Restore 的影响 问题 解决方案 方案1 方案 2 参考资料 情景 IP 角色 192.168.1.61 Primary 192.168.1.59 Second ...
- CentOs7安装部署Sonar环境(JDK1.8+MySql5.7+sonarqube7.8)
sonarqube安装前环境准备JDK1.8.MySql5.7. 一.JDK安装 1.下载jdk #打开下面的网址,选择 jdk-8u371-linux-x64.tar.gz 进行下载 (8u371版 ...
- 记录一个在写项目中遇到的Maven依赖无法导入的问题
记录一个在写项目中遇到的Maven依赖无法导入的问题 项目是一个父项目做依赖管理,三个子项目,今天遇到一个问题: 子项目中导入的依赖,怎么都导入不进去,maven仓库中已经有了,idea提示也没有问题 ...
- Linux系统运维之MYSQL数据库集群部署(主从复制)
一.介绍 Mysql主从复制,前段时间生产环境部署了一套主从复制的架构,当时现找了很多资料,现在记录下 二.拓扑图 三.环境以及软件版本 主机名 IP 操作系统 角色 软件版本 MysqlDB_Mas ...
- 百度Amis+React低代码实践
背景 在项目中有集成低代码平台的想法,经过多方对比最后选择了 amis,主要是需要通过 amis 进行页面配置,导出 json 供移动端和 PC 端进行渲染,所以接下来讲一下近两周研究 amis 的新 ...
- 【HTML】TinyMCE 编辑器
HTML编辑器 一.页面效果 二.引入JS.CSS <!DOCTYPE html> <html lang="en"> <head> <me ...
- [GIT]解决:failed to push some refs to ...(过程重现)
本问题有很多种情况,解决方法也很多,本文只针对笔者本人的自身诉求和情况,选择了一种适合我的解决方法.仅供参考. 1 问题描述 johnnyzen@XXDSSS MINGW64 /e/source_co ...