首页 > 其他分享 >使用shuffle sharding增加容错性

使用shuffle sharding增加容错性

时间:2023-09-20 09:44:55浏览次数:38  
标签:deckSize shuffle 队列 worker handSize 容错性 分片 sharding

使用shuffle sharding增加容错性

最近在看kubernetes的API Priority and Fairness,它使用shuffle sharding来为请求选择处理队列,以此防止高吞吐量流挤占低吞吐量流,进而造成请求延迟的问题。

介绍

首先看下什么是shuffle sharding,下面内容来自aws的Workload isolation using shuffle-sharding

首先来看如何使用一般分片方式来让系统具备可扩展性和弹性。

假设有一个8 workers节点的水平可扩展的系统或服务,下图红线表示达到这些节点的请求,worker可以是服务,队列或数据库等。

image

如果没有任何分片,则要求每个worker能够处理所有请求。这种方式高效且具备一定的冗余性。如果一个worker出现故障,则可以将它的任务分配到剩余的7个worker上。此时可能需要增加一定的系统容量。但如果突然出现大量请求,如DDoS攻击,可能会导致级联故障。下面两张图展示了故障是如何升级的。

image

首先会影响第一台worker,随后会级联到其他workers上,最终导致整个服务不可用。

image

为了防止故障转移,通常可以使用分片方式,如将workers分为4个分片,以效率换取影响度。下面两张图展示了如何使用分片来限制DDoS攻击。

image

本例中,每个分片包含2个workers,并按照资源(如域名)进行切片。此时的系统仍然具有冗余性,但由于每个分片只有2个workers,因此可能需要增加容量来避免故障。

image

通过这种方式降低了故障影响范围。这里有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。

image

如果彩虹用户分配的worker 1和worker 4出现了问题(如恶意请求或请求泛红等),则此问题只会影响本虚拟分片,但不会影响到其他shuffle shard。事实上,最多只会有另外一个shuffle shard会受到影响(即另外一个服务都部署到了worker 1和worker 4)。如果请求方具有容错性,则可以继续使用剩余分片继续提供服务。

image

换句话说,当彩虹客户所在的节点因为出现问题或受到攻击而无法提供服务时,不会影响到其他节点。对于客户而言,虽然玫瑰客户和向日葵客户都和彩虹客户共享了worker,但并没有导致其服务中断,玫瑰客户仍然可以继续使用workers 8提供服务,而向日葵客户可以继续使用worker 6提供服务。

image

当出现上述问题时,虽然失去了四分之一的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
}
  1. 首先使用func NewDealer(deckSize, handSize int)初始化一个实例,以kubernetes的APF功能为例,deckSize为队列数,handSize表示为一条流分配的队列数量

  2. 使用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
}

标签:deckSize,shuffle,队列,worker,handSize,容错性,分片,sharding
From: https://www.cnblogs.com/charlieroro/p/17703031.html

相关文章

  • shardingdb:支持分片和并发读写的 GoLevelDB
    概述shardingdb是一个开源包,旨在为GoLevelDB增加分片和并发读写功能。它可以作为LevelDB的替代品,方便地集成到现有项目中。本博客将介绍shardingdb及其功能,并介绍如何在您的项目中使用它。特点-分片支持:shardingdb使您能够将数据分布在多个LevelDB实例中,提高性能和......
  • MongoDB Sharding深入学习
    对于MongoDB的Sharding(分片)技术并不陌生,但是发现里面其实还是有不少值得深入学习的东西。笔记整理一下发上来跟大家分享。-----------------------------------一、MongoDB分片机制:1、一个分片包含数据的某一子集。若某一分片包含多台服务器。则每台服务器都拥有完整的数据副本。......
  • C语言编程的结构化要求和正确性与容错性要求
    一、结构化要求(1)禁止出现两条等价的支路。(2)禁止使用GOTO跳转语句。(3)用IF语句来强调只执行两组语句中的一组。禁止ELSEGOTO和ELSERETURN。(4)用CASE实现多路分支。(5)避免从循环引出多个出口。(6)3.6函数只有一个出口。(7)不使用条件赋值语句。(8)避免不必要的分支。(9)不要轻易用条件......
  • Spring Boot集成Sharding JDBC分库分表
    背景近期公司购物车项目需要使用ShardingJDBC分表,特记录下。ps:未分库依赖引入<!--sharding-sphereVersion:4.1.1--><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><ver......
  • ZS Shuffles Cards 题解
    ZSShufflesCards题解我们把每一次抽一些数字牌再抽到joker视作一局游戏。每局期望轮数首先考虑\(f_i\)表示每一局游戏抽出\(i\)张牌的概率。那么就是先抽出\(i-1\)张数字牌,再抽出一张joker。概率就是:\[f_i=\fracm{n+m-i+1}\prod_{k=0}^{i-2}......
  • Spring Boot集成Sharding JDBC分库分表
    背景近期公司购物车项目需要使用ShardingJDBC分表,特记录下。ps:未分库依赖引入<!--sharding-sphereVersion:4.1.1--><dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId><ver......
  • Sharding自定义分片策略
    公司分库分表使用用户id,主键后3位拼接用户id后三位,现把相关分片规则自定义简易组件使用一、参数配置引用者可以配置主键字段与用户字段命名,配置分片日志记录等packagecom.ypshengxian.shardingslice.properties;importorg.springframework.beans.factory.annotation.Val......
  • Hadoop:哪个数据节点是最近的数据节点来检索数据以及节点如何实现容错性
    Q1whocandecidewhichDataNodeistheclosestdatanodetoretrievethedata?当客户端要读一个文件的某个数据块时,它就需要向NameNode节点询问这个数据块存储在哪些DataNode节点上,这个过程如下图:当然,客户端至少需要向NameNode传输三个参数:文件路径、读取文件的起始位置、......
  • sharding-jdbc配置
    一、概念先行1)SQL相关的逻辑表:水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为2张表,分别是t_order_0到t_order_1,他们的逻辑表名为t_order。真实表:在分片的数据库中真实存在的物理表。例:示例中的t_order_0到t_order_1数据节点:数据分片的最小单......
  • 算法-10--python shuffle函数_python中shuffle()方法的功能详解
     pythonshuffle函数_python中shuffle()方法的功能详解: python的概率分布中,洗牌算法是通过shuffle()方法实现的,shuffle()方法将列表的所有元素打乱,随机排列。Python既可以使用random.shuffle对列表进行洗牌,也可以使用random.shuffle随机播放字符串列表,本文向大家介绍python中......