/* 遍历slots,获取正在迁移中的slots的迁移结果并该结果计入本次的迁移统计 */ for _, m := range ctx.slots { if m.Action.State != models.ActionNothing { assigned[m.Action.TargetId]++ } }
/* 按照平均值计算每个group可以分到的slots的数量(总量为1024) */ var lowerBound = MaxSlotNum / len(groupIds)
/* 遍历slots,统计需要迁移的slots信息 */ for _, m := range ctx.slots { /* 对于处于迁移状态中的slots不执行任何操作 */ if m.Action.State != models.ActionNothing { continue } /* 当前的slots属于集群中的一个group */ if m.GroupId != 0 { /* slot所归属group中的slots的数量小于group的平均值,则需要往这个group中分配新的slot */ if groupSize(m.GroupId) < lowerBound { assigned[m.GroupId]++ } else { /* slot所归属group中的slots的数量大于group的平均值,则需要将这个slot移出它所归属的group */ pendings[m.GroupId] = append(pendings[m.GroupId], m.Id) } } }
/* 创建一个自定义比较器的红黑树,这棵树代表着需要进行slots迁移的所有group * key是group的id,slots最少的在左面,slots最多的在右面,key是group的id */ var tree = rbtree.NewWith(func(x, y interface{})int { var gid1 = x.(int) var gid2 = y.(int) if gid1 != gid2 { if d := groupSize(gid1) - groupSize(gid2); d != 0 { return d } return gid1 - gid2 } return0 }) for _, gid := range groupIds { tree.Put(gid, nil) }
/* 遍历所有的slots */ for _, m := range ctx.slots { /* 对于处于迁移状态中的slots不执行任何操作 */ if m.Action.State != models.ActionNothing { continue } if m.GroupId != 0 { continue }
/* 有一些slots不属于任何group,需要将这些slots分配给slots最少的group,也就是红黑树左面的最小的group */ dest := tree.Left().Key.(int) tree.Remove(dest)
/* 遍历group,获取每一个group需要迁入多少个slots并将docking中的slots分配给对应的group, * plans就是最终的分配方案,将某一个slot分配给某一个group */ for _, gid := range groupIds { var in = -moveout[gid] for i := 0; i < in && len(docking) != 0; i++ { plans[docking[0]] = gid docking = docking[1:] } }
/* 审批该方案 */ if !confirm { return plans, nil }
/* 存储slots与group的分配方案后续执行 */ var slotIds []int for sid, _ := range plans { slotIds = append(slotIds, sid) } sort.Ints(slotIds)
for _, sid := range slotIds { m, err := ctx.getSlotMapping(sid) if err != nil { returnnil, err } defer s.dirtySlotsCache(m.Id)
// 按照每个Group可分配Slots的最大限制,统计Group中需要迁入/出的Slots信息 for _, m := range slots { if m.GroupID != 0 { if groupSize(m.GroupID) < lowerBound { assigned[m.GroupID]++ } else { pendings[m.GroupID] = append(pendings[m.GroupID], m.ID) } } }
// 依据现有的Group中Slots的数量构建红黑树 var tree = rbtree.NewWith(func(x, y interface{})int { var gid1 = x.(int) var gid2 = y.(int) if gid1 != gid2 { if d := groupSize(gid1) - groupSize(gid2); d != 0 { return d } return gid1 - gid2 } return0 }) for _, gid := range groupIds { tree.Put(gid, nil) } // fmt.Println("rbtree is ", tree.String())
// 统计无主的slots for _, m := range slots { if m.GroupID != 0 { continue } dest := tree.Left().Key.(int) tree.Remove(dest)
// 统计分配Slots for tree.Size() >= 2 { from := tree.Right().Key.(int) tree.Remove(from)
iflen(pendings[from]) == moveout[from] { continue } dest := tree.Left().Key.(int) tree.Remove(dest)
var ( fromSize = groupSize(from) destSize = groupSize(dest) ) if fromSize <= lowerBound { break } if destSize >= upperBound { break } if d := fromSize - destSize; d <= 1 { break } moveout[from]++ moveout[dest]--
tree.Put(from, nil) tree.Put(dest, nil) }
for gid, n := range moveout { if n < 0 { continue } if n > 0 { sids := pendings[gid] sort.Sort(sort.Reverse(sort.IntSlice(sids))) docking = append(docking, sids[0:n]...) pendings[gid] = sids[n:] } delete(moveout, gid) } sort.Ints(docking)
// 构建迁移方案 var plans = make(map[int]int) for _, gid := range groupIds { var in = -moveout[gid] for i := 0; i < in && len(docking) != 0; i++ { plans[docking[0]] = gid docking = docking[1:] } }
return plans, nil }
funcmain() { groupIds := []int{1, 2, 3} slots := make([]Slot, MaxSlotNum) for i := range slots { slots[i].ID = i }
plans, _ := SlotsRebalance(groupIds, slots[:10]) for k, v := range plans { slots[k].GroupID = v }